3rdparty: Move rapidyaml v0.6.0 in-tree

Also c4core v0.2.0, fast-float v6.1.1.
This commit is contained in:
Stenzek 2024-06-10 15:54:12 +10:00 committed by Connor McLaughlin
parent d6c4a9a4d5
commit 5c59288b39
97 changed files with 31759 additions and 167 deletions

4
.gitmodules vendored
View File

@ -2,7 +2,3 @@
path = 3rdparty/wil
url = https://github.com/microsoft/wil.git
branch = master
[submodule "3rdparty/rapidyaml/rapidyaml"]
path = 3rdparty/rapidyaml/rapidyaml
url = https://github.com/biojppm/rapidyaml.git
branch = master

2
3rdparty/fast_float/AUTHORS vendored Normal file
View File

@ -0,0 +1,2 @@
Daniel Lemire
João Paulo Magalhaes

3
3rdparty/fast_float/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,3 @@
add_library(fast_float INTERFACE)
target_include_directories(fast_float INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")

10
3rdparty/fast_float/CONTRIBUTORS vendored Normal file
View File

@ -0,0 +1,10 @@
Eugene Golushkov
Maksim Kita
Marcin Wojdyr
Neal Richardson
Tim Paine
Fabio Pellacini
Lénárd Szolnoki
Jan Pharago
Maya Warrier
Taha Khokhar

190
3rdparty/fast_float/LICENSE-APACHE vendored Normal file
View File

@ -0,0 +1,190 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2021 The fast_float authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

23
3rdparty/fast_float/LICENSE-BOOST vendored Normal file
View File

@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

27
3rdparty/fast_float/LICENSE-MIT vendored Normal file
View File

@ -0,0 +1,27 @@
MIT License
Copyright (c) 2021 The fast_float authors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

412
3rdparty/fast_float/README.md vendored Normal file
View File

@ -0,0 +1,412 @@
## fast_float number parsing library: 4x faster than strtod
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/fast_float.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:fast_float)
[![Ubuntu 22.04 CI (GCC 11)](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml/badge.svg)](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml)
The fast_float library provides fast header-only implementations for the C++ from_chars
functions for `float` and `double` types as well as integer types. These functions convert ASCII strings representing decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including
round to even). In our experience, these `fast_float` functions many times faster than comparable number-parsing functions from existing C++ standard libraries.
Specifically, `fast_float` provides the following two functions to parse floating-point numbers with a C++17-like syntax (the library itself only requires C++11):
```C++
from_chars_result from_chars(const char* first, const char* last, float& value, ...);
from_chars_result from_chars(const char* first, const char* last, double& value, ...);
```
You can also parse integer types:
The return type (`from_chars_result`) is defined as the struct:
```C++
struct from_chars_result {
const char* ptr;
std::errc ec;
};
```
It parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
a locale-independent format equivalent to the C++17 from_chars function.
The resulting floating-point value is the closest floating-point values (using either float or double),
using the "round to even" convention for values that would otherwise fall right in-between two values.
That is, we provide exact parsing according to the IEEE standard.
Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the
parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned
`ec` contains a representative error, otherwise the default (`std::errc()`) value is stored.
The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`).
It will parse infinity and nan values.
Example:
``` C++
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
const std::string input = "3.1416 xyz ";
double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
if(answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}
```
You can parse delimited numbers:
```C++
const std::string input = "234532.3426362,7869234.9823,324562.645";
double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
if(answer.ec != std::errc()) {
// check error
}
// we have result == 234532.3426362.
if(answer.ptr[0] != ',') {
// unexpected delimiter
}
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
if(answer.ec != std::errc()) {
// check error
}
// we have result == 7869234.9823.
if(answer.ptr[0] != ',') {
// unexpected delimiter
}
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
if(answer.ec != std::errc()) {
// check error
}
// we have result == 324562.645.
```
Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
the type `fast_float::chars_format`. It is a bitset value: we check whether
`fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set
to determine whether we allow the fixed point and scientific notation respectively.
The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
The library seeks to follow the C++17 (see [20.19.3](http://eel.is/c++draft/charconv.from.chars).(7.1)) specification.
* The `from_chars` function does not skip leading white-space characters.
* [A leading `+` sign](https://en.cppreference.com/w/cpp/utility/from_chars) is forbidden.
* It is generally impossible to represent a decimal value exactly as binary floating-point number (`float` and `double` types). We seek the nearest value. We round to an even mantissa when we are in-between two binary floating-point numbers.
Furthermore, we have the following restrictions:
* We only support `float` and `double` types at this time.
* We only support the decimal format: we do not support hexadecimal strings.
* For values that are either very large or very small (e.g., `1e9999`), we represent it using the infinity or negative infinity value and the returned `ec` is set to `std::errc::result_out_of_range`.
We support Visual Studio, macOS, Linux, freeBSD. We support big and little endian. We support 32-bit and 64-bit systems.
We assume that the rounding mode is set to nearest (`std::fegetround() == FE_TONEAREST`).
## Integer types
You can also parse integer types using different bases (e.g., 2, 10, 16). The following code will
print the number 22250738585072012 three times:
```C++
uint64_t i;
const char str[] = "22250738585072012";
auto answer = fast_float::from_chars(str, str + strlen(str), i);
if (answer.ec != std::errc()) {
std::cerr << "parsing failure\n";
return EXIT_FAILURE;
}
std::cout << "parsed the number "<< i << std::endl;
const char binstr[] = "1001111000011001110110111001001010110100111000110001100";
answer = fast_float::from_chars(binstr, binstr + strlen(binstr), i, 2);
if (answer.ec != std::errc()) {
std::cerr << "parsing failure\n";
return EXIT_FAILURE;
}
std::cout << "parsed the number "<< i << std::endl;
const char hexstr[] = "4f0cedc95a718c";
answer = fast_float::from_chars(hexstr, hexstr + strlen(hexstr), i, 16);
if (answer.ec != std::errc()) {
std::cerr << "parsing failure\n";
return EXIT_FAILURE;
}
std::cout << "parsed the number "<< i << std::endl;
```
## C++20: compile-time evaluation (constexpr)
In C++20, you may use `fast_float::from_chars` to parse strings
at compile-time, as in the following example:
```C++
// consteval forces compile-time evaluation of the function in C++20.
consteval double parse(std::string_view input) {
double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
if(answer.ec != std::errc()) { return -1.0; }
return result;
}
// This function should compile to a function which
// merely returns 3.1415.
constexpr double constexptest() {
return parse("3.1415 input");
}
```
## C++23: Fixed width floating-point types
The library also supports fixed-width floating-point types such as `std::float32_t` and `std::float64_t`. E.g., you can write:
```C++
std::float32_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
``````
## Non-ASCII Inputs
We also support UTF-16 and UTF-32 inputs, as well as ASCII/UTF-8, as in the following example:
``` C++
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
const std::u16string input = u"3.1416 xyz ";
double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
if(answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}
```
## Advanced options: using commas as decimal separator, JSON and Fortran
The C++ standard stipulate that `from_chars` has to be locale-independent. In
particular, the decimal separator has to be the period (`.`). However,
some users still want to use the `fast_float` library with in a locale-dependent
manner. Using a separate function called `from_chars_advanced`, we allow the users
to pass a `parse_options` instance which contains a custom decimal separator (e.g.,
the comma). You may use it as follows.
```C++
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
const std::string input = "3,1416 xyz ";
double result;
fast_float::parse_options options{fast_float::chars_format::general, ','};
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}
```
You can also parse Fortran-like inputs:
```C++
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
const std::string input = "1d+4";
double result;
fast_float::parse_options options{ fast_float::chars_format::fortran };
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
if((answer.ec != std::errc()) || ((result != 10000))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}
```
You may also enforce the JSON format ([RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259#section-6)):
```C++
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
const std::string input = "+.1"; // not valid
double result;
fast_float::parse_options options{ fast_float::chars_format::json };
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
if(answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; }
return EXIT_SUCCESS;
}
```
By default the JSON format does not allow `inf`:
```C++
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
const std::string input = "inf"; // not valid in JSON
double result;
fast_float::parse_options options{ fast_float::chars_format::json };
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
if(answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; }
}
```
You can allow it with a non-standard `json_or_infnan` variant:
```C++
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
const std::string input = "inf"; // not valid in JSON but we allow it with json_or_infnan
double result;
fast_float::parse_options options{ fast_float::chars_format::json_or_infnan };
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
if(answer.ec != std::errc() || (!std::isinf(result))) { std::cerr << "should have parsed infinity\n"; return EXIT_FAILURE; }
return EXIT_SUCCESS;
}
``````
## Relation With Other Work
The fast_float library is part of:
- GCC (as of version 12): the `from_chars` function in GCC relies on fast_float.
- [WebKit](https://github.com/WebKit/WebKit), the engine behind Safari (Apple's web browser)
The fastfloat algorithm is part of the [LLVM standard libraries](https://github.com/llvm/llvm-project/commit/87c016078ad72c46505461e4ff8bfa04819fe7ba).
There is a [derived implementation part of AdaCore](https://github.com/AdaCore/VSS).
The fast_float library provides a performance similar to that of the [fast_double_parser](https://github.com/lemire/fast_double_parser) library but using an updated algorithm reworked from the ground up, and while offering an API more in line with the expectations of C++ programmers. The fast_double_parser library is part of the [Microsoft LightGBM machine-learning framework](https://github.com/microsoft/LightGBM).
## References
- Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Practice and Experience 51 (8), 2021.
- Noble Mushtak, Daniel Lemire, [Fast Number Parsing Without Fallback](https://arxiv.org/abs/2212.06644), Software: Practice and Experience 53 (7), 2023.
## Other programming languages
- [There is an R binding](https://github.com/eddelbuettel/rcppfastfloat) called `rcppfastfloat`.
- [There is a Rust port of the fast_float library](https://github.com/aldanor/fast-float-rust/) called `fast-float-rust`.
- [There is a Java port of the fast_float library](https://github.com/wrandelshofer/FastDoubleParser) called `FastDoubleParser`. It used for important systems such as [Jackson](https://github.com/FasterXML/jackson-core).
- [There is a C# port of the fast_float library](https://github.com/CarlVerret/csFastFloat) called `csFastFloat`.
## Users
The fast_float library is used by [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times. It is also used by [ClickHouse](https://github.com/ClickHouse/ClickHouse) and by [Google Jsonnet](https://github.com/google/jsonnet). It is part of GCC (as of GCC 12). It is part of WebKit (Safari).
## How fast is it?
It can parse random floating-point numbers at a speed of 1 GB/s on some systems. We find that it is often twice as fast as the best available competitor, and many times faster than many standard-library implementations.
<img src="http://lemire.me/blog/wp-content/uploads/2020/11/fastfloat_speed.png" width="400">
```
$ ./build/benchmarks/benchmark
# parsing random integers in the range [0,1)
volume = 2.09808 MB
netlib : 271.18 MB/s (+/- 1.2 %) 12.93 Mfloat/s
doubleconversion : 225.35 MB/s (+/- 1.2 %) 10.74 Mfloat/s
strtod : 190.94 MB/s (+/- 1.6 %) 9.10 Mfloat/s
abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfloat/s
fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s
```
See https://github.com/lemire/simple_fastfloat_benchmark for our benchmarking code.
## Video
[![Go Systems 2020](http://img.youtube.com/vi/AVXgvlMeIm4/0.jpg)](http://www.youtube.com/watch?v=AVXgvlMeIm4)<br />
## Using as a CMake dependency
This library is header-only by design. The CMake file provides the `fast_float` target
which is merely a pointer to the `include` directory.
If you drop the `fast_float` repository in your CMake project, you should be able to use
it in this manner:
```cmake
add_subdirectory(fast_float)
target_link_libraries(myprogram PUBLIC fast_float)
```
Or you may want to retrieve the dependency automatically if you have a sufficiently recent version of CMake (3.11 or better at least):
```cmake
FetchContent_Declare(
fast_float
GIT_REPOSITORY https://github.com/lemire/fast_float.git
GIT_TAG tags/v1.1.2
GIT_SHALLOW TRUE)
FetchContent_MakeAvailable(fast_float)
target_link_libraries(myprogram PUBLIC fast_float)
```
You should change the `GIT_TAG` line so that you recover the version you wish to use.
## Using as single header
The script `script/amalgamate.py` may be used to generate a single header
version of the library if so desired.
Just run the script from the root directory of this repository.
You can customize the license type and output file if desired as described in
the command line help.
You may directly download automatically generated single-header files:
https://github.com/fastfloat/fast_float/releases/download/v6.1.1/fast_float.h
## RFC 7159
If you need support for RFC 7159 (JSON standard), you may want to consider using the [fast_double_parser](https://github.com/lemire/fast_double_parser/) library instead.
## Credit
Though this work is inspired by many different people, this work benefited especially from exchanges with
Michael Eisel, who motivated the original research with his key insights, and with Nigel Tao who provided
invaluable feedback. Rémy Oudompheng first implemented a fast path we use in the case of long digits.
The library includes code adapted from Google Wuffs (written by Nigel Tao) which was originally published
under the Apache 2.0 license.
## License
<sup>
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
2.0</a> or <a href="LICENSE-MIT">MIT license</a> or <a href="LICENSE-BOOST">BOOST license</a> .
</sup>
<br>
<sub>
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in this repository by you, as defined in the Apache-2.0 license,
shall be triple licensed as above, without any additional terms or conditions.
</sub>

View File

@ -0,0 +1,546 @@
#ifndef FASTFLOAT_ASCII_NUMBER_H
#define FASTFLOAT_ASCII_NUMBER_H
#include <cctype>
#include <cstdint>
#include <cstring>
#include <iterator>
#include <limits>
#include <type_traits>
#include "float_common.h"
#ifdef FASTFLOAT_SSE2
#include <emmintrin.h>
#endif
#ifdef FASTFLOAT_NEON
#include <arm_neon.h>
#endif
namespace fast_float {
template <typename UC>
fastfloat_really_inline constexpr bool has_simd_opt() {
#ifdef FASTFLOAT_HAS_SIMD
return std::is_same<UC, char16_t>::value;
#else
return false;
#endif
}
// Next function can be micro-optimized, but compilers are entirely
// able to optimize it well.
template <typename UC>
fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
return !(c > UC('9') || c < UC('0'));
}
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
return (val & 0xFF00000000000000) >> 56
| (val & 0x00FF000000000000) >> 40
| (val & 0x0000FF0000000000) >> 24
| (val & 0x000000FF00000000) >> 8
| (val & 0x00000000FF000000) << 8
| (val & 0x0000000000FF0000) << 24
| (val & 0x000000000000FF00) << 40
| (val & 0x00000000000000FF) << 56;
}
// Read 8 UC into a u64. Truncates UC if not char.
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
uint64_t read8_to_u64(const UC *chars) {
if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) {
uint64_t val = 0;
for(int i = 0; i < 8; ++i) {
val |= uint64_t(uint8_t(*chars)) << (i*8);
++chars;
}
return val;
}
uint64_t val;
::memcpy(&val, chars, sizeof(uint64_t));
#if FASTFLOAT_IS_BIG_ENDIAN == 1
// Need to read as-if the number was in little-endian order.
val = byteswap(val);
#endif
return val;
}
#ifdef FASTFLOAT_SSE2
fastfloat_really_inline
uint64_t simd_read8_to_u64(const __m128i data) {
FASTFLOAT_SIMD_DISABLE_WARNINGS
const __m128i packed = _mm_packus_epi16(data, data);
#ifdef FASTFLOAT_64BIT
return uint64_t(_mm_cvtsi128_si64(packed));
#else
uint64_t value;
// Visual Studio + older versions of GCC don't support _mm_storeu_si64
_mm_storel_epi64(reinterpret_cast<__m128i*>(&value), packed);
return value;
#endif
FASTFLOAT_SIMD_RESTORE_WARNINGS
}
fastfloat_really_inline
uint64_t simd_read8_to_u64(const char16_t* chars) {
FASTFLOAT_SIMD_DISABLE_WARNINGS
return simd_read8_to_u64(_mm_loadu_si128(reinterpret_cast<const __m128i*>(chars)));
FASTFLOAT_SIMD_RESTORE_WARNINGS
}
#elif defined(FASTFLOAT_NEON)
fastfloat_really_inline
uint64_t simd_read8_to_u64(const uint16x8_t data) {
FASTFLOAT_SIMD_DISABLE_WARNINGS
uint8x8_t utf8_packed = vmovn_u16(data);
return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0);
FASTFLOAT_SIMD_RESTORE_WARNINGS
}
fastfloat_really_inline
uint64_t simd_read8_to_u64(const char16_t* chars) {
FASTFLOAT_SIMD_DISABLE_WARNINGS
return simd_read8_to_u64(vld1q_u16(reinterpret_cast<const uint16_t*>(chars)));
FASTFLOAT_SIMD_RESTORE_WARNINGS
}
#endif // FASTFLOAT_SSE2
// MSVC SFINAE is broken pre-VS2017
#if defined(_MSC_VER) && _MSC_VER <= 1900
template <typename UC>
#else
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
#endif
// dummy for compile
uint64_t simd_read8_to_u64(UC const*) {
return 0;
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void write_u64(uint8_t *chars, uint64_t val) {
if (cpp20_and_in_constexpr()) {
for(int i = 0; i < 8; ++i) {
*chars = uint8_t(val);
val >>= 8;
++chars;
}
return;
}
#if FASTFLOAT_IS_BIG_ENDIAN == 1
// Need to read as-if the number was in little-endian order.
val = byteswap(val);
#endif
::memcpy(chars, &val, sizeof(uint64_t));
}
// credit @aqrit
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
uint32_t parse_eight_digits_unrolled(uint64_t val) {
const uint64_t mask = 0x000000FF000000FF;
const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
val -= 0x3030303030303030;
val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
return uint32_t(val);
}
// Call this if chars are definitely 8 digits.
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
uint32_t parse_eight_digits_unrolled(UC const * chars) noexcept {
if (cpp20_and_in_constexpr() || !has_simd_opt<UC>()) {
return parse_eight_digits_unrolled(read8_to_u64(chars)); // truncation okay
}
return parse_eight_digits_unrolled(simd_read8_to_u64(chars));
}
// credit @aqrit
fastfloat_really_inline constexpr bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
0x8080808080808080));
}
#ifdef FASTFLOAT_HAS_SIMD
// Call this if chars might not be 8 digits.
// Using this style (instead of is_made_of_eight_digits_fast() then parse_eight_digits_unrolled())
// ensures we don't load SIMD registers twice.
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
bool simd_parse_if_eight_digits_unrolled(const char16_t* chars, uint64_t& i) noexcept {
if (cpp20_and_in_constexpr()) {
return false;
}
#ifdef FASTFLOAT_SSE2
FASTFLOAT_SIMD_DISABLE_WARNINGS
const __m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i*>(chars));
// (x - '0') <= 9
// http://0x80.pl/articles/simd-parsing-int-sequences.html
const __m128i t0 = _mm_add_epi16(data, _mm_set1_epi16(32720));
const __m128i t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759));
if (_mm_movemask_epi8(t1) == 0) {
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
return true;
}
else return false;
FASTFLOAT_SIMD_RESTORE_WARNINGS
#elif defined(FASTFLOAT_NEON)
FASTFLOAT_SIMD_DISABLE_WARNINGS
const uint16x8_t data = vld1q_u16(reinterpret_cast<const uint16_t*>(chars));
// (x - '0') <= 9
// http://0x80.pl/articles/simd-parsing-int-sequences.html
const uint16x8_t t0 = vsubq_u16(data, vmovq_n_u16('0'));
const uint16x8_t mask = vcltq_u16(t0, vmovq_n_u16('9' - '0' + 1));
if (vminvq_u16(mask) == 0xFFFF) {
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
return true;
}
else return false;
FASTFLOAT_SIMD_RESTORE_WARNINGS
#else
(void)chars; (void)i;
return false;
#endif // FASTFLOAT_SSE2
}
#endif // FASTFLOAT_HAS_SIMD
// MSVC SFINAE is broken pre-VS2017
#if defined(_MSC_VER) && _MSC_VER <= 1900
template <typename UC>
#else
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
#endif
// dummy for compile
bool simd_parse_if_eight_digits_unrolled(UC const*, uint64_t&) {
return 0;
}
template <typename UC, FASTFLOAT_ENABLE_IF(!std::is_same<UC, char>::value) = 0>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void loop_parse_if_eight_digits(const UC*& p, const UC* const pend, uint64_t& i) {
if (!has_simd_opt<UC>()) {
return;
}
while ((std::distance(p, pend) >= 8) && simd_parse_if_eight_digits_unrolled(p, i)) { // in rare cases, this will overflow, but that's ok
p += 8;
}
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void loop_parse_if_eight_digits(const char*& p, const char* const pend, uint64_t& i) {
// optimizes better than parse_if_eight_digits_unrolled() for UC = char.
while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(read8_to_u64(p))) {
i = i * 100000000 + parse_eight_digits_unrolled(read8_to_u64(p)); // in rare cases, this will overflow, but that's ok
p += 8;
}
}
template <typename UC>
struct parsed_number_string_t {
int64_t exponent{0};
uint64_t mantissa{0};
UC const * lastmatch{nullptr};
bool negative{false};
bool valid{false};
bool too_many_digits{false};
// contains the range of the significant digits
span<const UC> integer{}; // non-nullable
span<const UC> fraction{}; // nullable
};
using byte_span = span<const char>;
using parsed_number_string = parsed_number_string_t<char>;
// Assuming that you use no more than 19 digits, this will
// parse an ASCII string.
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, parse_options_t<UC> options) noexcept {
chars_format const fmt = options.format;
UC const decimal_point = options.decimal_point;
parsed_number_string_t<UC> answer;
answer.valid = false;
answer.too_many_digits = false;
answer.negative = (*p == UC('-'));
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
if ((*p == UC('-')) || (!(fmt & FASTFLOAT_JSONFMT) && *p == UC('+'))) {
#else
if (*p == UC('-')) { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
#endif
++p;
if (p == pend) {
return answer;
}
if (fmt & FASTFLOAT_JSONFMT) {
if (!is_integer(*p)) { // a sign must be followed by an integer
return answer;
}
} else {
if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot
return answer;
}
}
}
UC const * const start_digits = p;
uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
while ((p != pend) && is_integer(*p)) {
// a multiplication by 10 is cheaper than an arbitrary integer
// multiplication
i = 10 * i +
uint64_t(*p - UC('0')); // might overflow, we will handle the overflow later
++p;
}
UC const * const end_of_integer_part = p;
int64_t digit_count = int64_t(end_of_integer_part - start_digits);
answer.integer = span<const UC>(start_digits, size_t(digit_count));
if (fmt & FASTFLOAT_JSONFMT) {
// at least 1 digit in integer part, without leading zeros
if (digit_count == 0 || (start_digits[0] == UC('0') && digit_count > 1)) {
return answer;
}
}
int64_t exponent = 0;
const bool has_decimal_point = (p != pend) && (*p == decimal_point);
if (has_decimal_point) {
++p;
UC const * before = p;
// can occur at most twice without overflowing, but let it occur more, since
// for integers with many digits, digit parsing is the primary bottleneck.
loop_parse_if_eight_digits(p, pend, i);
while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - UC('0'));
++p;
i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
}
exponent = before - p;
answer.fraction = span<const UC>(before, size_t(p - before));
digit_count -= exponent;
}
if (fmt & FASTFLOAT_JSONFMT) {
// at least 1 digit in fractional part
if (has_decimal_point && exponent == 0) {
return answer;
}
}
else if (digit_count == 0) { // we must have encountered at least one integer!
return answer;
}
int64_t exp_number = 0; // explicit exponential part
if ( ((fmt & chars_format::scientific) &&
(p != pend) &&
((UC('e') == *p) || (UC('E') == *p)))
||
((fmt & FASTFLOAT_FORTRANFMT) &&
(p != pend) &&
((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) || (UC('D') == *p)))) {
UC const * location_of_e = p;
if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) || (UC('D') == *p)) {
++p;
}
bool neg_exp = false;
if ((p != pend) && (UC('-') == *p)) {
neg_exp = true;
++p;
} else if ((p != pend) && (UC('+') == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
++p;
}
if ((p == pend) || !is_integer(*p)) {
if(!(fmt & chars_format::fixed)) {
// We are in error.
return answer;
}
// Otherwise, we will be ignoring the 'e'.
p = location_of_e;
} else {
while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - UC('0'));
if (exp_number < 0x10000000) {
exp_number = 10 * exp_number + digit;
}
++p;
}
if(neg_exp) { exp_number = - exp_number; }
exponent += exp_number;
}
} else {
// If it scientific and not fixed, we have to bail out.
if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; }
}
answer.lastmatch = p;
answer.valid = true;
// If we frequently had to deal with long strings of digits,
// we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon.
//
// We can deal with up to 19 digits.
if (digit_count > 19) { // this is uncommon
// It is possible that the integer had an overflow.
// We have to handle the case where we have 0.0000somenumber.
// We need to be mindful of the case where we only have zeroes...
// E.g., 0.000000000...000.
UC const * start = start_digits;
while ((start != pend) && (*start == UC('0') || *start == decimal_point)) {
if(*start == UC('0')) { digit_count --; }
start++;
}
if (digit_count > 19) {
answer.too_many_digits = true;
// Let us start again, this time, avoiding overflows.
// We don't need to check if is_integer, since we use the
// pre-tokenized spans from above.
i = 0;
p = answer.integer.ptr;
UC const* int_end = p + answer.integer.len();
const uint64_t minimal_nineteen_digit_integer{ 1000000000000000000 };
while ((i < minimal_nineteen_digit_integer) && (p != int_end)) {
i = i * 10 + uint64_t(*p - UC('0'));
++p;
}
if (i >= minimal_nineteen_digit_integer) { // We have a big integers
exponent = end_of_integer_part - p + exp_number;
}
else { // We have a value with a fractional component.
p = answer.fraction.ptr;
UC const* frac_end = p + answer.fraction.len();
while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
i = i * 10 + uint64_t(*p - UC('0'));
++p;
}
exponent = answer.fraction.ptr - p + exp_number;
}
// We have now corrected both exponent and i, to a truncated value
}
}
answer.exponent = exponent;
answer.mantissa = i;
return answer;
}
template <typename T, typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> parse_int_string(UC const* p, UC const* pend, T& value, int base) {
from_chars_result_t<UC> answer;
UC const* const first = p;
bool negative = (*p == UC('-'));
if (!std::is_signed<T>::value && negative) {
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
return answer;
}
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
if ((*p == UC('-')) || (*p == UC('+'))) {
#else
if (*p == UC('-')) {
#endif
++p;
}
UC const* const start_num = p;
while (p!= pend && *p == UC('0')) {
++p;
}
const bool has_leading_zeros = p > start_num;
UC const* const start_digits = p;
uint64_t i = 0;
if (base == 10) {
loop_parse_if_eight_digits(p, pend, i); // use SIMD if possible
}
while (p != pend) {
uint8_t digit = ch_to_digit(*p);
if (digit >= base) {
break;
}
i = uint64_t(base) * i + digit; // might overflow, check this later
p++;
}
size_t digit_count = size_t(p - start_digits);
if (digit_count == 0) {
if (has_leading_zeros) {
value = 0;
answer.ec = std::errc();
answer.ptr = p;
}
else {
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
}
return answer;
}
answer.ptr = p;
// check u64 overflow
size_t max_digits = max_digits_u64(base);
if (digit_count > max_digits) {
answer.ec = std::errc::result_out_of_range;
return answer;
}
// this check can be eliminated for all other types, but they will all require a max_digits(base) equivalent
if (digit_count == max_digits && i < min_safe_u64(base)) {
answer.ec = std::errc::result_out_of_range;
return answer;
}
// check other types overflow
if (!std::is_same<T, uint64_t>::value) {
if (i > uint64_t(std::numeric_limits<T>::max()) + uint64_t(negative)) {
answer.ec = std::errc::result_out_of_range;
return answer;
}
}
if (negative) {
#ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(push)
#pragma warning(disable: 4146)
#endif
// this weird workaround is required because:
// - converting unsigned to signed when its value is greater than signed max is UB pre-C++23.
// - reinterpret_casting (~i + 1) would work, but it is not constexpr
// this is always optimized into a neg instruction (note: T is an integer type)
value = T(-std::numeric_limits<T>::max() - T(i - uint64_t(std::numeric_limits<T>::max())));
#ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(pop)
#endif
}
else { value = T(i); }
answer.ec = std::errc();
return answer;
}
} // namespace fast_float
#endif

View File

@ -0,0 +1,617 @@
#ifndef FASTFLOAT_BIGINT_H
#define FASTFLOAT_BIGINT_H
#include <algorithm>
#include <cstdint>
#include <climits>
#include <cstring>
#include "float_common.h"
namespace fast_float {
// the limb width: we want efficient multiplication of double the bits in
// limb, or for 64-bit limbs, at least 64-bit multiplication where we can
// extract the high and low parts efficiently. this is every 64-bit
// architecture except for sparc, which emulates 128-bit multiplication.
// we might have platforms where `CHAR_BIT` is not 8, so let's avoid
// doing `8 * sizeof(limb)`.
#if defined(FASTFLOAT_64BIT) && !defined(__sparc)
#define FASTFLOAT_64BIT_LIMB 1
typedef uint64_t limb;
constexpr size_t limb_bits = 64;
#else
#define FASTFLOAT_32BIT_LIMB
typedef uint32_t limb;
constexpr size_t limb_bits = 32;
#endif
typedef span<limb> limb_span;
// number of bits in a bigint. this needs to be at least the number
// of bits required to store the largest bigint, which is
// `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or
// ~3600 bits, so we round to 4000.
constexpr size_t bigint_bits = 4000;
constexpr size_t bigint_limbs = bigint_bits / limb_bits;
// vector-like type that is allocated on the stack. the entire
// buffer is pre-allocated, and only the length changes.
template <uint16_t size>
struct stackvec {
limb data[size];
// we never need more than 150 limbs
uint16_t length{0};
stackvec() = default;
stackvec(const stackvec &) = delete;
stackvec &operator=(const stackvec &) = delete;
stackvec(stackvec &&) = delete;
stackvec &operator=(stackvec &&other) = delete;
// create stack vector from existing limb span.
FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) {
FASTFLOAT_ASSERT(try_extend(s));
}
FASTFLOAT_CONSTEXPR14 limb& operator[](size_t index) noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index];
}
FASTFLOAT_CONSTEXPR14 const limb& operator[](size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index];
}
// index from the end of the container
FASTFLOAT_CONSTEXPR14 const limb& rindex(size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length);
size_t rindex = length - index - 1;
return data[rindex];
}
// set the length, without bounds checking.
FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept {
length = uint16_t(len);
}
constexpr size_t len() const noexcept {
return length;
}
constexpr bool is_empty() const noexcept {
return length == 0;
}
constexpr size_t capacity() const noexcept {
return size;
}
// append item to vector, without bounds checking
FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept {
data[length] = value;
length++;
}
// append item to vector, returning if item was added
FASTFLOAT_CONSTEXPR14 bool try_push(limb value) noexcept {
if (len() < capacity()) {
push_unchecked(value);
return true;
} else {
return false;
}
}
// add items to the vector, from a span, without bounds checking
FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept {
limb* ptr = data + length;
std::copy_n(s.ptr, s.len(), ptr);
set_len(len() + s.len());
}
// try to add items to the vector, returning if items were added
FASTFLOAT_CONSTEXPR20 bool try_extend(limb_span s) noexcept {
if (len() + s.len() <= capacity()) {
extend_unchecked(s);
return true;
} else {
return false;
}
}
// resize the vector, without bounds checking
// if the new size is longer than the vector, assign value to each
// appended item.
FASTFLOAT_CONSTEXPR20
void resize_unchecked(size_t new_len, limb value) noexcept {
if (new_len > len()) {
size_t count = new_len - len();
limb* first = data + len();
limb* last = first + count;
::std::fill(first, last, value);
set_len(new_len);
} else {
set_len(new_len);
}
}
// try to resize the vector, returning if the vector was resized.
FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept {
if (new_len > capacity()) {
return false;
} else {
resize_unchecked(new_len, value);
return true;
}
}
// check if any limbs are non-zero after the given index.
// this needs to be done in reverse order, since the index
// is relative to the most significant limbs.
FASTFLOAT_CONSTEXPR14 bool nonzero(size_t index) const noexcept {
while (index < len()) {
if (rindex(index) != 0) {
return true;
}
index++;
}
return false;
}
// normalize the big integer, so most-significant zero limbs are removed.
FASTFLOAT_CONSTEXPR14 void normalize() noexcept {
while (len() > 0 && rindex(0) == 0) {
length--;
}
}
};
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
uint64_t empty_hi64(bool& truncated) noexcept {
truncated = false;
return 0;
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
uint64_t uint64_hi64(uint64_t r0, bool& truncated) noexcept {
truncated = false;
int shl = leading_zeroes(r0);
return r0 << shl;
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept {
int shl = leading_zeroes(r0);
if (shl == 0) {
truncated = r1 != 0;
return r0;
} else {
int shr = 64 - shl;
truncated = (r1 << shl) != 0;
return (r0 << shl) | (r1 >> shr);
}
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
uint64_t uint32_hi64(uint32_t r0, bool& truncated) noexcept {
return uint64_hi64(r0, truncated);
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
uint64_t uint32_hi64(uint32_t r0, uint32_t r1, bool& truncated) noexcept {
uint64_t x0 = r0;
uint64_t x1 = r1;
return uint64_hi64((x0 << 32) | x1, truncated);
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool& truncated) noexcept {
uint64_t x0 = r0;
uint64_t x1 = r1;
uint64_t x2 = r2;
return uint64_hi64(x0, (x1 << 32) | x2, truncated);
}
// add two small integers, checking for overflow.
// we want an efficient operation. for msvc, where
// we don't have built-in intrinsics, this is still
// pretty fast.
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
limb scalar_add(limb x, limb y, bool& overflow) noexcept {
limb z;
// gcc and clang
#if defined(__has_builtin)
#if __has_builtin(__builtin_add_overflow)
if (!cpp20_and_in_constexpr()) {
overflow = __builtin_add_overflow(x, y, &z);
return z;
}
#endif
#endif
// generic, this still optimizes correctly on MSVC.
z = x + y;
overflow = z < x;
return z;
}
// multiply two small integers, getting both the high and low bits.
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
limb scalar_mul(limb x, limb y, limb& carry) noexcept {
#ifdef FASTFLOAT_64BIT_LIMB
#if defined(__SIZEOF_INT128__)
// GCC and clang both define it as an extension.
__uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry);
carry = limb(z >> limb_bits);
return limb(z);
#else
// fallback, no native 128-bit integer multiplication with carry.
// on msvc, this optimizes identically, somehow.
value128 z = full_multiplication(x, y);
bool overflow;
z.low = scalar_add(z.low, carry, overflow);
z.high += uint64_t(overflow); // cannot overflow
carry = z.high;
return z.low;
#endif
#else
uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry);
carry = limb(z >> limb_bits);
return limb(z);
#endif
}
// add scalar value to bigint starting from offset.
// used in grade school multiplication
template <uint16_t size>
inline FASTFLOAT_CONSTEXPR20
bool small_add_from(stackvec<size>& vec, limb y, size_t start) noexcept {
size_t index = start;
limb carry = y;
bool overflow;
while (carry != 0 && index < vec.len()) {
vec[index] = scalar_add(vec[index], carry, overflow);
carry = limb(overflow);
index += 1;
}
if (carry != 0) {
FASTFLOAT_TRY(vec.try_push(carry));
}
return true;
}
// add scalar value to bigint.
template <uint16_t size>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
bool small_add(stackvec<size>& vec, limb y) noexcept {
return small_add_from(vec, y, 0);
}
// multiply bigint by scalar value.
template <uint16_t size>
inline FASTFLOAT_CONSTEXPR20
bool small_mul(stackvec<size>& vec, limb y) noexcept {
limb carry = 0;
for (size_t index = 0; index < vec.len(); index++) {
vec[index] = scalar_mul(vec[index], y, carry);
}
if (carry != 0) {
FASTFLOAT_TRY(vec.try_push(carry));
}
return true;
}
// add bigint to bigint starting from index.
// used in grade school multiplication
template <uint16_t size>
FASTFLOAT_CONSTEXPR20
bool large_add_from(stackvec<size>& x, limb_span y, size_t start) noexcept {
// the effective x buffer is from `xstart..x.len()`, so exit early
// if we can't get that current range.
if (x.len() < start || y.len() > x.len() - start) {
FASTFLOAT_TRY(x.try_resize(y.len() + start, 0));
}
bool carry = false;
for (size_t index = 0; index < y.len(); index++) {
limb xi = x[index + start];
limb yi = y[index];
bool c1 = false;
bool c2 = false;
xi = scalar_add(xi, yi, c1);
if (carry) {
xi = scalar_add(xi, 1, c2);
}
x[index + start] = xi;
carry = c1 | c2;
}
// handle overflow
if (carry) {
FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start));
}
return true;
}
// add bigint to bigint.
template <uint16_t size>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
bool large_add_from(stackvec<size>& x, limb_span y) noexcept {
return large_add_from(x, y, 0);
}
// grade-school multiplication algorithm
template <uint16_t size>
FASTFLOAT_CONSTEXPR20
bool long_mul(stackvec<size>& x, limb_span y) noexcept {
limb_span xs = limb_span(x.data, x.len());
stackvec<size> z(xs);
limb_span zs = limb_span(z.data, z.len());
if (y.len() != 0) {
limb y0 = y[0];
FASTFLOAT_TRY(small_mul(x, y0));
for (size_t index = 1; index < y.len(); index++) {
limb yi = y[index];
stackvec<size> zi;
if (yi != 0) {
// re-use the same buffer throughout
zi.set_len(0);
FASTFLOAT_TRY(zi.try_extend(zs));
FASTFLOAT_TRY(small_mul(zi, yi));
limb_span zis = limb_span(zi.data, zi.len());
FASTFLOAT_TRY(large_add_from(x, zis, index));
}
}
}
x.normalize();
return true;
}
// grade-school multiplication algorithm
template <uint16_t size>
FASTFLOAT_CONSTEXPR20
bool large_mul(stackvec<size>& x, limb_span y) noexcept {
if (y.len() == 1) {
FASTFLOAT_TRY(small_mul(x, y[0]));
} else {
FASTFLOAT_TRY(long_mul(x, y));
}
return true;
}
template <typename = void>
struct pow5_tables {
static constexpr uint32_t large_step = 135;
static constexpr uint64_t small_power_of_5[] = {
1UL, 5UL, 25UL, 125UL, 625UL, 3125UL, 15625UL, 78125UL, 390625UL,
1953125UL, 9765625UL, 48828125UL, 244140625UL, 1220703125UL,
6103515625UL, 30517578125UL, 152587890625UL, 762939453125UL,
3814697265625UL, 19073486328125UL, 95367431640625UL, 476837158203125UL,
2384185791015625UL, 11920928955078125UL, 59604644775390625UL,
298023223876953125UL, 1490116119384765625UL, 7450580596923828125UL,
};
#ifdef FASTFLOAT_64BIT_LIMB
constexpr static limb large_power_of_5[] = {
1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL,
10482974169319127550UL, 198276706040285095UL};
#else
constexpr static limb large_power_of_5[] = {
4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U,
1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U};
#endif
};
template <typename T>
constexpr uint32_t pow5_tables<T>::large_step;
template <typename T>
constexpr uint64_t pow5_tables<T>::small_power_of_5[];
template <typename T>
constexpr limb pow5_tables<T>::large_power_of_5[];
// big integer type. implements a small subset of big integer
// arithmetic, using simple algorithms since asymptotically
// faster algorithms are slower for a small number of limbs.
// all operations assume the big-integer is normalized.
struct bigint : pow5_tables<> {
// storage of the limbs, in little-endian order.
stackvec<bigint_limbs> vec;
FASTFLOAT_CONSTEXPR20 bigint(): vec() {}
bigint(const bigint &) = delete;
bigint &operator=(const bigint &) = delete;
bigint(bigint &&) = delete;
bigint &operator=(bigint &&other) = delete;
FASTFLOAT_CONSTEXPR20 bigint(uint64_t value): vec() {
#ifdef FASTFLOAT_64BIT_LIMB
vec.push_unchecked(value);
#else
vec.push_unchecked(uint32_t(value));
vec.push_unchecked(uint32_t(value >> 32));
#endif
vec.normalize();
}
// get the high 64 bits from the vector, and if bits were truncated.
// this is to get the significant digits for the float.
FASTFLOAT_CONSTEXPR20 uint64_t hi64(bool& truncated) const noexcept {
#ifdef FASTFLOAT_64BIT_LIMB
if (vec.len() == 0) {
return empty_hi64(truncated);
} else if (vec.len() == 1) {
return uint64_hi64(vec.rindex(0), truncated);
} else {
uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated);
truncated |= vec.nonzero(2);
return result;
}
#else
if (vec.len() == 0) {
return empty_hi64(truncated);
} else if (vec.len() == 1) {
return uint32_hi64(vec.rindex(0), truncated);
} else if (vec.len() == 2) {
return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated);
} else {
uint64_t result = uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated);
truncated |= vec.nonzero(3);
return result;
}
#endif
}
// compare two big integers, returning the large value.
// assumes both are normalized. if the return value is
// negative, other is larger, if the return value is
// positive, this is larger, otherwise they are equal.
// the limbs are stored in little-endian order, so we
// must compare the limbs in ever order.
FASTFLOAT_CONSTEXPR20 int compare(const bigint& other) const noexcept {
if (vec.len() > other.vec.len()) {
return 1;
} else if (vec.len() < other.vec.len()) {
return -1;
} else {
for (size_t index = vec.len(); index > 0; index--) {
limb xi = vec[index - 1];
limb yi = other.vec[index - 1];
if (xi > yi) {
return 1;
} else if (xi < yi) {
return -1;
}
}
return 0;
}
}
// shift left each limb n bits, carrying over to the new limb
// returns true if we were able to shift all the digits.
FASTFLOAT_CONSTEXPR20 bool shl_bits(size_t n) noexcept {
// Internally, for each item, we shift left by n, and add the previous
// right shifted limb-bits.
// For example, we transform (for u8) shifted left 2, to:
// b10100100 b01000010
// b10 b10010001 b00001000
FASTFLOAT_DEBUG_ASSERT(n != 0);
FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8);
size_t shl = n;
size_t shr = limb_bits - shl;
limb prev = 0;
for (size_t index = 0; index < vec.len(); index++) {
limb xi = vec[index];
vec[index] = (xi << shl) | (prev >> shr);
prev = xi;
}
limb carry = prev >> shr;
if (carry != 0) {
return vec.try_push(carry);
}
return true;
}
// move the limbs left by `n` limbs.
FASTFLOAT_CONSTEXPR20 bool shl_limbs(size_t n) noexcept {
FASTFLOAT_DEBUG_ASSERT(n != 0);
if (n + vec.len() > vec.capacity()) {
return false;
} else if (!vec.is_empty()) {
// move limbs
limb* dst = vec.data + n;
const limb* src = vec.data;
std::copy_backward(src, src + vec.len(), dst + vec.len());
// fill in empty limbs
limb* first = vec.data;
limb* last = first + n;
::std::fill(first, last, 0);
vec.set_len(n + vec.len());
return true;
} else {
return true;
}
}
// move the limbs left by `n` bits.
FASTFLOAT_CONSTEXPR20 bool shl(size_t n) noexcept {
size_t rem = n % limb_bits;
size_t div = n / limb_bits;
if (rem != 0) {
FASTFLOAT_TRY(shl_bits(rem));
}
if (div != 0) {
FASTFLOAT_TRY(shl_limbs(div));
}
return true;
}
// get the number of leading zeros in the bigint.
FASTFLOAT_CONSTEXPR20 int ctlz() const noexcept {
if (vec.is_empty()) {
return 0;
} else {
#ifdef FASTFLOAT_64BIT_LIMB
return leading_zeroes(vec.rindex(0));
#else
// no use defining a specialized leading_zeroes for a 32-bit type.
uint64_t r0 = vec.rindex(0);
return leading_zeroes(r0 << 32);
#endif
}
}
// get the number of bits in the bigint.
FASTFLOAT_CONSTEXPR20 int bit_length() const noexcept {
int lz = ctlz();
return int(limb_bits * vec.len()) - lz;
}
FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept {
return small_mul(vec, y);
}
FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept {
return small_add(vec, y);
}
// multiply as if by 2 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept {
return shl(exp);
}
// multiply as if by 5 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept {
// multiply by a power of 5
size_t large_length = sizeof(large_power_of_5) / sizeof(limb);
limb_span large = limb_span(large_power_of_5, large_length);
while (exp >= large_step) {
FASTFLOAT_TRY(large_mul(vec, large));
exp -= large_step;
}
#ifdef FASTFLOAT_64BIT_LIMB
uint32_t small_step = 27;
limb max_native = 7450580596923828125UL;
#else
uint32_t small_step = 13;
limb max_native = 1220703125U;
#endif
while (exp >= small_step) {
FASTFLOAT_TRY(small_mul(vec, max_native));
exp -= small_step;
}
if (exp != 0) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc
// This is similar to https://github.com/llvm/llvm-project/issues/47746,
// except the workaround described there don't work here
FASTFLOAT_TRY(
small_mul(vec, limb(((void)small_power_of_5[0], small_power_of_5[exp])))
);
}
return true;
}
// multiply as if by 10 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow10(uint32_t exp) noexcept {
FASTFLOAT_TRY(pow5(exp));
return pow2(exp);
}
};
} // namespace fast_float
#endif

View File

@ -0,0 +1,40 @@
#ifndef FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
#define FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
#ifdef __has_include
#if __has_include(<version>)
#include <version>
#endif
#endif
// Testing for https://wg21.link/N3652, adopted in C++14
#if __cpp_constexpr >= 201304
#define FASTFLOAT_CONSTEXPR14 constexpr
#else
#define FASTFLOAT_CONSTEXPR14
#endif
#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L
#define FASTFLOAT_HAS_BIT_CAST 1
#else
#define FASTFLOAT_HAS_BIT_CAST 0
#endif
#if defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1
#else
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0
#endif
// Testing for relevant C++20 constexpr library features
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED \
&& FASTFLOAT_HAS_BIT_CAST \
&& __cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/
#define FASTFLOAT_CONSTEXPR20 constexpr
#define FASTFLOAT_IS_CONSTEXPR 1
#else
#define FASTFLOAT_CONSTEXPR20
#define FASTFLOAT_IS_CONSTEXPR 0
#endif
#endif // FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H

View File

@ -0,0 +1,189 @@
#ifndef FASTFLOAT_DECIMAL_TO_BINARY_H
#define FASTFLOAT_DECIMAL_TO_BINARY_H
#include "float_common.h"
#include "fast_table.h"
#include <cfloat>
#include <cinttypes>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <cstring>
namespace fast_float {
// This will compute or rather approximate w * 5**q and return a pair of 64-bit words approximating
// the result, with the "high" part corresponding to the most significant bits and the
// low part corresponding to the least significant bits.
//
template <int bit_precision>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
value128 compute_product_approximation(int64_t q, uint64_t w) {
const int index = 2 * int(q - powers::smallest_power_of_five);
// For small values of q, e.g., q in [0,27], the answer is always exact because
// The line value128 firstproduct = full_multiplication(w, power_of_five_128[index]);
// gives the exact answer.
value128 firstproduct = full_multiplication(w, powers::power_of_five_128[index]);
static_assert((bit_precision >= 0) && (bit_precision <= 64), " precision should be in (0,64]");
constexpr uint64_t precision_mask = (bit_precision < 64) ?
(uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
: uint64_t(0xFFFFFFFFFFFFFFFF);
if((firstproduct.high & precision_mask) == precision_mask) { // could further guard with (lower + w < lower)
// regarding the second product, we only need secondproduct.high, but our expectation is that the compiler will optimize this extra work away if needed.
value128 secondproduct = full_multiplication(w, powers::power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) {
firstproduct.high++;
}
}
return firstproduct;
}
namespace detail {
/**
* For q in (0,350), we have that
* f = (((152170 + 65536) * q ) >> 16);
* is equal to
* floor(p) + q
* where
* p = log(5**q)/log(2) = q * log(5)/log(2)
*
* For negative values of q in (-400,0), we have that
* f = (((152170 + 65536) * q ) >> 16);
* is equal to
* -ceil(p) + q
* where
* p = log(5**-q)/log(2) = -q * log(5)/log(2)
*/
constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept {
return (((152170 + 65536) * q) >> 16) + 63;
}
} // namespace detail
// create an adjusted mantissa, biased by the invalid power2
// for significant digits already multiplied by 10 ** q.
template <typename binary>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
int hilz = int(w >> 63) ^ 1;
adjusted_mantissa answer;
answer.mantissa = w << hilz;
int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent();
answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 + invalid_am_bias);
return answer;
}
// w * 10 ** q, without rounding the representation up.
// the power2 in the exponent will be adjusted by invalid_am_bias.
template <typename binary>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept {
int lz = leading_zeroes(w);
w <<= lz;
value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
return compute_error_scaled<binary>(q, product.high, lz);
}
// w * 10 ** q
// The returned value should be a valid ieee64 number that simply need to be packed.
// However, in some very rare cases, the computation will fail. In such cases, we
// return an adjusted_mantissa with a negative power of 2: the caller should recompute
// in such cases.
template <typename binary>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
adjusted_mantissa answer;
if ((w == 0) || (q < binary::smallest_power_of_ten())) {
answer.power2 = 0;
answer.mantissa = 0;
// result should be zero
return answer;
}
if (q > binary::largest_power_of_ten()) {
// we want to get infinity:
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
return answer;
}
// At this point in time q is in [powers::smallest_power_of_five, powers::largest_power_of_five].
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(w);
w <<= lz;
// The required precision is binary::mantissa_explicit_bits() + 3 because
// 1. We need the implicit bit
// 2. We need an extra bit for rounding purposes
// 3. We might lose a bit due to the "upperbit" routine (result too small, requiring a shift)
value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
// The computed 'product' is always sufficient.
// Mathematical proof:
// Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to appear)
// See script/mushtak_lemire.py
// The "compute_product_approximation" function can be slightly slower than a branchless approach:
// value128 product = compute_product(q, w);
// but in practice, we can win big with the compute_product_approximation if its additional branch
// is easily predicted. Which is best is data specific.
int upperbit = int(product.high >> 63);
answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3);
answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz - binary::minimum_exponent());
if (answer.power2 <= 0) { // we have a subnormal?
// Here have that answer.power2 <= 0 so -answer.power2 >= 0
if(-answer.power2 + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
answer.power2 = 0;
answer.mantissa = 0;
// result should be zero
return answer;
}
// next line is safe because -answer.power2 + 1 < 64
answer.mantissa >>= -answer.power2 + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
answer.mantissa += (answer.mantissa & 1); // round up
answer.mantissa >>= 1;
// There is a weird scenario where we don't have a subnormal but just.
// Suppose we start with 2.2250738585072013e-308, we end up
// with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal
// whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round
// up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer
// subnormal, but we can only know this after rounding.
// So we only declare a subnormal if we are smaller than the threshold.
answer.power2 = (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) ? 0 : 1;
return answer;
}
// usually, we round *up*, but if we fall right in between and and we have an
// even basis, we need to round down
// We are only concerned with the cases where 5**q fits in single 64-bit word.
if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) && (q <= binary::max_exponent_round_to_even()) &&
((answer.mantissa & 3) == 1) ) { // we may fall between two floats!
// To be in-between two floats we need that in doing
// answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3);
// ... we dropped out only zeroes. But if this happened, then we can go back!!!
if((answer.mantissa << (upperbit + 64 - binary::mantissa_explicit_bits() - 3)) == product.high) {
answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up
}
}
answer.mantissa += (answer.mantissa & 1); // round up
answer.mantissa >>= 1;
if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) {
answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits());
answer.power2++; // undo previous addition
}
answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits());
if (answer.power2 >= binary::infinite_power()) { // infinity
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
}
return answer;
}
} // namespace fast_float
#endif

View File

@ -0,0 +1,426 @@
#ifndef FASTFLOAT_DIGIT_COMPARISON_H
#define FASTFLOAT_DIGIT_COMPARISON_H
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <iterator>
#include "float_common.h"
#include "bigint.h"
#include "ascii_number.h"
namespace fast_float {
// 1e0 to 1e19
constexpr static uint64_t powers_of_ten_uint64[] = {
1UL, 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL, 100000000UL,
1000000000UL, 10000000000UL, 100000000000UL, 1000000000000UL, 10000000000000UL,
100000000000000UL, 1000000000000000UL, 10000000000000000UL, 100000000000000000UL,
1000000000000000000UL, 10000000000000000000UL};
// calculate the exponent, in scientific notation, of the number.
// this algorithm is not even close to optimized, but it has no practical
// effect on performance: in order to have a faster algorithm, we'd need
// to slow down performance for faster algorithms, and this is still fast.
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
int32_t scientific_exponent(parsed_number_string_t<UC> & num) noexcept {
uint64_t mantissa = num.mantissa;
int32_t exponent = int32_t(num.exponent);
while (mantissa >= 10000) {
mantissa /= 10000;
exponent += 4;
}
while (mantissa >= 100) {
mantissa /= 100;
exponent += 2;
}
while (mantissa >= 10) {
mantissa /= 10;
exponent += 1;
}
return exponent;
}
// this converts a native floating-point number to an extended-precision float.
template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
adjusted_mantissa to_extended(T value) noexcept {
using equiv_uint = typename binary_format<T>::equiv_uint;
constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
adjusted_mantissa am;
int32_t bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent();
equiv_uint bits;
#if FASTFLOAT_HAS_BIT_CAST
bits = std::bit_cast<equiv_uint>(value);
#else
::memcpy(&bits, &value, sizeof(T));
#endif
if ((bits & exponent_mask) == 0) {
// denormal
am.power2 = 1 - bias;
am.mantissa = bits & mantissa_mask;
} else {
// normal
am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits());
am.power2 -= bias;
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
}
return am;
}
// get the extended precision value of the halfway point between b and b+u.
// we are given a native float that represents b, so we need to adjust it
// halfway between b and b+u.
template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
adjusted_mantissa to_extended_halfway(T value) noexcept {
adjusted_mantissa am = to_extended(value);
am.mantissa <<= 1;
am.mantissa += 1;
am.power2 -= 1;
return am;
}
// round an extended-precision float to the nearest machine float.
template <typename T, typename callback>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
void round(adjusted_mantissa& am, callback cb) noexcept {
int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
if (-am.power2 >= mantissa_shift) {
// have a denormal float
int32_t shift = -am.power2 + 1;
cb(am, std::min<int32_t>(shift, 64));
// check for round-up: if rounding-nearest carried us to the hidden bit.
am.power2 = (am.mantissa < (uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) ? 0 : 1;
return;
}
// have a normal float, use the default shift.
cb(am, mantissa_shift);
// check for carry
if (am.mantissa >= (uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) {
am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
am.power2++;
}
// check for infinite: we could have carried to an infinite power
am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
if (am.power2 >= binary_format<T>::infinite_power()) {
am.power2 = binary_format<T>::infinite_power();
am.mantissa = 0;
}
}
template <typename callback>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept {
const uint64_t mask
= (shift == 64)
? UINT64_MAX
: (uint64_t(1) << shift) - 1;
const uint64_t halfway
= (shift == 0)
? 0
: uint64_t(1) << (shift - 1);
uint64_t truncated_bits = am.mantissa & mask;
bool is_above = truncated_bits > halfway;
bool is_halfway = truncated_bits == halfway;
// shift digits into position
if (shift == 64) {
am.mantissa = 0;
} else {
am.mantissa >>= shift;
}
am.power2 += shift;
bool is_odd = (am.mantissa & 1) == 1;
am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
void round_down(adjusted_mantissa& am, int32_t shift) noexcept {
if (shift == 64) {
am.mantissa = 0;
} else {
am.mantissa >>= shift;
}
am.power2 += shift;
}
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void skip_zeros(UC const * & first, UC const * last) noexcept {
uint64_t val;
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= int_cmp_len<UC>()) {
::memcpy(&val, first, sizeof(uint64_t));
if (val != int_cmp_zeros<UC>()) {
break;
}
first += int_cmp_len<UC>();
}
while (first != last) {
if (*first != UC('0')) {
break;
}
first++;
}
}
// determine if any non-zero digits were truncated.
// all characters must be valid digits.
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
bool is_truncated(UC const * first, UC const * last) noexcept {
// do 8-bit optimizations, can just compare to 8 literal 0s.
uint64_t val;
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= int_cmp_len<UC>()) {
::memcpy(&val, first, sizeof(uint64_t));
if (val != int_cmp_zeros<UC>()) {
return true;
}
first += int_cmp_len<UC>();
}
while (first != last) {
if (*first != UC('0')) {
return true;
}
++first;
}
return false;
}
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
bool is_truncated(span<const UC> s) noexcept {
return is_truncated(s.ptr, s.ptr + s.len());
}
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void parse_eight_digits(const UC*& p, limb& value, size_t& counter, size_t& count) noexcept {
value = value * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
counter += 8;
count += 8;
}
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
void parse_one_digit(UC const *& p, limb& value, size_t& counter, size_t& count) noexcept {
value = value * 10 + limb(*p - UC('0'));
p++;
counter++;
count++;
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void add_native(bigint& big, limb power, limb value) noexcept {
big.mul(power);
big.add(value);
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void round_up_bigint(bigint& big, size_t& count) noexcept {
// need to round-up the digits, but need to avoid rounding
// ....9999 to ...10000, which could cause a false halfway point.
add_native(big, 10, 1);
count++;
}
// parse the significant digits into a big integer
template <typename UC>
inline FASTFLOAT_CONSTEXPR20
void parse_mantissa(bigint& result, parsed_number_string_t<UC>& num, size_t max_digits, size_t& digits) noexcept {
// try to minimize the number of big integer and scalar multiplication.
// therefore, try to parse 8 digits at a time, and multiply by the largest
// scalar value (9 or 19 digits) for each step.
size_t counter = 0;
digits = 0;
limb value = 0;
#ifdef FASTFLOAT_64BIT_LIMB
size_t step = 19;
#else
size_t step = 9;
#endif
// process all integer digits.
UC const * p = num.integer.ptr;
UC const * pend = p + num.integer.len();
skip_zeros(p, pend);
// process all digits, in increments of step per loop
while (p != pend) {
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) {
parse_eight_digits(p, value, counter, digits);
}
while (counter < step && p != pend && digits < max_digits) {
parse_one_digit(p, value, counter, digits);
}
if (digits == max_digits) {
// add the temporary value, then check if we've truncated any digits
add_native(result, limb(powers_of_ten_uint64[counter]), value);
bool truncated = is_truncated(p, pend);
if (num.fraction.ptr != nullptr) {
truncated |= is_truncated(num.fraction);
}
if (truncated) {
round_up_bigint(result, digits);
}
return;
} else {
add_native(result, limb(powers_of_ten_uint64[counter]), value);
counter = 0;
value = 0;
}
}
// add our fraction digits, if they're available.
if (num.fraction.ptr != nullptr) {
p = num.fraction.ptr;
pend = p + num.fraction.len();
if (digits == 0) {
skip_zeros(p, pend);
}
// process all digits, in increments of step per loop
while (p != pend) {
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) {
parse_eight_digits(p, value, counter, digits);
}
while (counter < step && p != pend && digits < max_digits) {
parse_one_digit(p, value, counter, digits);
}
if (digits == max_digits) {
// add the temporary value, then check if we've truncated any digits
add_native(result, limb(powers_of_ten_uint64[counter]), value);
bool truncated = is_truncated(p, pend);
if (truncated) {
round_up_bigint(result, digits);
}
return;
} else {
add_native(result, limb(powers_of_ten_uint64[counter]), value);
counter = 0;
value = 0;
}
}
}
if (counter != 0) {
add_native(result, limb(powers_of_ten_uint64[counter]), value);
}
}
template <typename T>
inline FASTFLOAT_CONSTEXPR20
adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent) noexcept {
FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent)));
adjusted_mantissa answer;
bool truncated;
answer.mantissa = bigmant.hi64(truncated);
int bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent();
answer.power2 = bigmant.bit_length() - 64 + bias;
round<T>(answer, [truncated](adjusted_mantissa& a, int32_t shift) {
round_nearest_tie_even(a, shift, [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
return is_above || (is_halfway && truncated) || (is_odd && is_halfway);
});
});
return answer;
}
// the scaling here is quite simple: we have, for the real digits `m * 10^e`,
// and for the theoretical digits `n * 2^f`. Since `e` is always negative,
// to scale them identically, we do `n * 2^f * 5^-f`, so we now have `m * 2^e`.
// we then need to scale by `2^(f- e)`, and then the two significant digits
// are of the same magnitude.
template <typename T>
inline FASTFLOAT_CONSTEXPR20
adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int32_t exponent) noexcept {
bigint& real_digits = bigmant;
int32_t real_exp = exponent;
// get the value of `b`, rounded down, and get a bigint representation of b+h
adjusted_mantissa am_b = am;
// gcc7 buf: use a lambda to remove the noexcept qualifier bug with -Wnoexcept-type.
round<T>(am_b, [](adjusted_mantissa&a, int32_t shift) { round_down(a, shift); });
T b;
to_float(false, am_b, b);
adjusted_mantissa theor = to_extended_halfway(b);
bigint theor_digits(theor.mantissa);
int32_t theor_exp = theor.power2;
// scale real digits and theor digits to be same power.
int32_t pow2_exp = theor_exp - real_exp;
uint32_t pow5_exp = uint32_t(-real_exp);
if (pow5_exp != 0) {
FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp));
}
if (pow2_exp > 0) {
FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp)));
} else if (pow2_exp < 0) {
FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp)));
}
// compare digits, and use it to director rounding
int ord = real_digits.compare(theor_digits);
adjusted_mantissa answer = am;
round<T>(answer, [ord](adjusted_mantissa& a, int32_t shift) {
round_nearest_tie_even(a, shift, [ord](bool is_odd, bool _, bool __) -> bool {
(void)_; // not needed, since we've done our comparison
(void)__; // not needed, since we've done our comparison
if (ord > 0) {
return true;
} else if (ord < 0) {
return false;
} else {
return is_odd;
}
});
});
return answer;
}
// parse the significant digits as a big integer to unambiguously round the
// the significant digits. here, we are trying to determine how to round
// an extended float representation close to `b+h`, halfway between `b`
// (the float rounded-down) and `b+u`, the next positive float. this
// algorithm is always correct, and uses one of two approaches. when
// the exponent is positive relative to the significant digits (such as
// 1234), we create a big-integer representation, get the high 64-bits,
// determine if any lower bits are truncated, and use that to direct
// rounding. in case of a negative exponent relative to the significant
// digits (such as 1.2345), we create a theoretical representation of
// `b` as a big-integer type, scaled to the same binary exponent as
// the actual digits. we then compare the big integer representations
// of both, and use that to direct rounding.
template <typename T, typename UC>
inline FASTFLOAT_CONSTEXPR20
adjusted_mantissa digit_comp(parsed_number_string_t<UC>& num, adjusted_mantissa am) noexcept {
// remove the invalid exponent bias
am.power2 -= invalid_am_bias;
int32_t sci_exp = scientific_exponent(num);
size_t max_digits = binary_format<T>::max_digits();
size_t digits = 0;
bigint bigmant;
parse_mantissa(bigmant, num, max_digits, digits);
// can't underflow, since digits is at most max_digits.
int32_t exponent = sci_exp + 1 - int32_t(digits);
if (exponent >= 0) {
return positive_digit_comp<T>(bigmant, exponent);
} else {
return negative_digit_comp<T>(bigmant, am, exponent);
}
}
} // namespace fast_float
#endif

View File

@ -0,0 +1,48 @@
#ifndef FASTFLOAT_FAST_FLOAT_H
#define FASTFLOAT_FAST_FLOAT_H
#include "float_common.h"
namespace fast_float {
/**
* This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
* a locale-indepent format equivalent to what is used by std::strtod in the default ("C") locale.
* The resulting floating-point value is the closest floating-point values (using either float or double),
* using the "round to even" convention for values that would otherwise fall right in-between two values.
* That is, we provide exact parsing according to the IEEE standard.
*
* Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the
* parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned
* `ec` contains a representative error, otherwise the default (`std::errc()`) value is stored.
*
* The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`).
*
* Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
* the type `fast_float::chars_format`. It is a bitset value: we check whether
* `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set
* to determine whether we allow the fixed point and scientific notation respectively.
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
*/
template<typename T, typename UC = char, typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>())>
FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
T &value, chars_format fmt = chars_format::general) noexcept;
/**
* Like from_chars, but accepts an `options` argument to govern number parsing.
*/
template<typename T, typename UC = char>
FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
T &value, parse_options_t<UC> options) noexcept;
/**
* from_chars for integer types.
*/
template <typename T, typename UC = char, typename = FASTFLOAT_ENABLE_IF(!is_supported_float_type<T>())>
FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars(UC const * first, UC const * last, T& value, int base = 10) noexcept;
} // namespace fast_float
#include "parse_number.h"
#endif // FASTFLOAT_FAST_FLOAT_H

View File

@ -0,0 +1,700 @@
#ifndef FASTFLOAT_FAST_TABLE_H
#define FASTFLOAT_FAST_TABLE_H
#include <cstdint>
namespace fast_float {
/**
* When mapping numbers from decimal to binary,
* we go from w * 10^q to m * 2^p but we have
* 10^q = 5^q * 2^q, so effectively
* we are trying to match
* w * 2^q * 5^q to m * 2^p. Thus the powers of two
* are not a concern since they can be represented
* exactly using the binary notation, only the powers of five
* affect the binary significand.
*/
/**
* The smallest non-zero float (binary64) is 2^-1074.
* We take as input numbers of the form w x 10^q where w < 2^64.
* We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076.
* However, we have that
* (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^-1074.
* Thus it is possible for a number of the form w * 10^-342 where
* w is a 64-bit value to be a non-zero floating-point number.
*********
* Any number of form w * 10^309 where w>= 1 is going to be
* infinite in binary64 so we never need to worry about powers
* of 5 greater than 308.
*/
template <class unused = void>
struct powers_template {
constexpr static int smallest_power_of_five = binary_format<double>::smallest_power_of_ten();
constexpr static int largest_power_of_five = binary_format<double>::largest_power_of_ten();
constexpr static int number_of_entries = 2 * (largest_power_of_five - smallest_power_of_five + 1);
// Powers of five from 5^-342 all the way to 5^308 rounded toward one.
constexpr static uint64_t power_of_five_128[number_of_entries] = {
0xeef453d6923bd65a,0x113faa2906a13b3f,
0x9558b4661b6565f8,0x4ac7ca59a424c507,
0xbaaee17fa23ebf76,0x5d79bcf00d2df649,
0xe95a99df8ace6f53,0xf4d82c2c107973dc,
0x91d8a02bb6c10594,0x79071b9b8a4be869,
0xb64ec836a47146f9,0x9748e2826cdee284,
0xe3e27a444d8d98b7,0xfd1b1b2308169b25,
0x8e6d8c6ab0787f72,0xfe30f0f5e50e20f7,
0xb208ef855c969f4f,0xbdbd2d335e51a935,
0xde8b2b66b3bc4723,0xad2c788035e61382,
0x8b16fb203055ac76,0x4c3bcb5021afcc31,
0xaddcb9e83c6b1793,0xdf4abe242a1bbf3d,
0xd953e8624b85dd78,0xd71d6dad34a2af0d,
0x87d4713d6f33aa6b,0x8672648c40e5ad68,
0xa9c98d8ccb009506,0x680efdaf511f18c2,
0xd43bf0effdc0ba48,0x212bd1b2566def2,
0x84a57695fe98746d,0x14bb630f7604b57,
0xa5ced43b7e3e9188,0x419ea3bd35385e2d,
0xcf42894a5dce35ea,0x52064cac828675b9,
0x818995ce7aa0e1b2,0x7343efebd1940993,
0xa1ebfb4219491a1f,0x1014ebe6c5f90bf8,
0xca66fa129f9b60a6,0xd41a26e077774ef6,
0xfd00b897478238d0,0x8920b098955522b4,
0x9e20735e8cb16382,0x55b46e5f5d5535b0,
0xc5a890362fddbc62,0xeb2189f734aa831d,
0xf712b443bbd52b7b,0xa5e9ec7501d523e4,
0x9a6bb0aa55653b2d,0x47b233c92125366e,
0xc1069cd4eabe89f8,0x999ec0bb696e840a,
0xf148440a256e2c76,0xc00670ea43ca250d,
0x96cd2a865764dbca,0x380406926a5e5728,
0xbc807527ed3e12bc,0xc605083704f5ecf2,
0xeba09271e88d976b,0xf7864a44c633682e,
0x93445b8731587ea3,0x7ab3ee6afbe0211d,
0xb8157268fdae9e4c,0x5960ea05bad82964,
0xe61acf033d1a45df,0x6fb92487298e33bd,
0x8fd0c16206306bab,0xa5d3b6d479f8e056,
0xb3c4f1ba87bc8696,0x8f48a4899877186c,
0xe0b62e2929aba83c,0x331acdabfe94de87,
0x8c71dcd9ba0b4925,0x9ff0c08b7f1d0b14,
0xaf8e5410288e1b6f,0x7ecf0ae5ee44dd9,
0xdb71e91432b1a24a,0xc9e82cd9f69d6150,
0x892731ac9faf056e,0xbe311c083a225cd2,
0xab70fe17c79ac6ca,0x6dbd630a48aaf406,
0xd64d3d9db981787d,0x92cbbccdad5b108,
0x85f0468293f0eb4e,0x25bbf56008c58ea5,
0xa76c582338ed2621,0xaf2af2b80af6f24e,
0xd1476e2c07286faa,0x1af5af660db4aee1,
0x82cca4db847945ca,0x50d98d9fc890ed4d,
0xa37fce126597973c,0xe50ff107bab528a0,
0xcc5fc196fefd7d0c,0x1e53ed49a96272c8,
0xff77b1fcbebcdc4f,0x25e8e89c13bb0f7a,
0x9faacf3df73609b1,0x77b191618c54e9ac,
0xc795830d75038c1d,0xd59df5b9ef6a2417,
0xf97ae3d0d2446f25,0x4b0573286b44ad1d,
0x9becce62836ac577,0x4ee367f9430aec32,
0xc2e801fb244576d5,0x229c41f793cda73f,
0xf3a20279ed56d48a,0x6b43527578c1110f,
0x9845418c345644d6,0x830a13896b78aaa9,
0xbe5691ef416bd60c,0x23cc986bc656d553,
0xedec366b11c6cb8f,0x2cbfbe86b7ec8aa8,
0x94b3a202eb1c3f39,0x7bf7d71432f3d6a9,
0xb9e08a83a5e34f07,0xdaf5ccd93fb0cc53,
0xe858ad248f5c22c9,0xd1b3400f8f9cff68,
0x91376c36d99995be,0x23100809b9c21fa1,
0xb58547448ffffb2d,0xabd40a0c2832a78a,
0xe2e69915b3fff9f9,0x16c90c8f323f516c,
0x8dd01fad907ffc3b,0xae3da7d97f6792e3,
0xb1442798f49ffb4a,0x99cd11cfdf41779c,
0xdd95317f31c7fa1d,0x40405643d711d583,
0x8a7d3eef7f1cfc52,0x482835ea666b2572,
0xad1c8eab5ee43b66,0xda3243650005eecf,
0xd863b256369d4a40,0x90bed43e40076a82,
0x873e4f75e2224e68,0x5a7744a6e804a291,
0xa90de3535aaae202,0x711515d0a205cb36,
0xd3515c2831559a83,0xd5a5b44ca873e03,
0x8412d9991ed58091,0xe858790afe9486c2,
0xa5178fff668ae0b6,0x626e974dbe39a872,
0xce5d73ff402d98e3,0xfb0a3d212dc8128f,
0x80fa687f881c7f8e,0x7ce66634bc9d0b99,
0xa139029f6a239f72,0x1c1fffc1ebc44e80,
0xc987434744ac874e,0xa327ffb266b56220,
0xfbe9141915d7a922,0x4bf1ff9f0062baa8,
0x9d71ac8fada6c9b5,0x6f773fc3603db4a9,
0xc4ce17b399107c22,0xcb550fb4384d21d3,
0xf6019da07f549b2b,0x7e2a53a146606a48,
0x99c102844f94e0fb,0x2eda7444cbfc426d,
0xc0314325637a1939,0xfa911155fefb5308,
0xf03d93eebc589f88,0x793555ab7eba27ca,
0x96267c7535b763b5,0x4bc1558b2f3458de,
0xbbb01b9283253ca2,0x9eb1aaedfb016f16,
0xea9c227723ee8bcb,0x465e15a979c1cadc,
0x92a1958a7675175f,0xbfacd89ec191ec9,
0xb749faed14125d36,0xcef980ec671f667b,
0xe51c79a85916f484,0x82b7e12780e7401a,
0x8f31cc0937ae58d2,0xd1b2ecb8b0908810,
0xb2fe3f0b8599ef07,0x861fa7e6dcb4aa15,
0xdfbdcece67006ac9,0x67a791e093e1d49a,
0x8bd6a141006042bd,0xe0c8bb2c5c6d24e0,
0xaecc49914078536d,0x58fae9f773886e18,
0xda7f5bf590966848,0xaf39a475506a899e,
0x888f99797a5e012d,0x6d8406c952429603,
0xaab37fd7d8f58178,0xc8e5087ba6d33b83,
0xd5605fcdcf32e1d6,0xfb1e4a9a90880a64,
0x855c3be0a17fcd26,0x5cf2eea09a55067f,
0xa6b34ad8c9dfc06f,0xf42faa48c0ea481e,
0xd0601d8efc57b08b,0xf13b94daf124da26,
0x823c12795db6ce57,0x76c53d08d6b70858,
0xa2cb1717b52481ed,0x54768c4b0c64ca6e,
0xcb7ddcdda26da268,0xa9942f5dcf7dfd09,
0xfe5d54150b090b02,0xd3f93b35435d7c4c,
0x9efa548d26e5a6e1,0xc47bc5014a1a6daf,
0xc6b8e9b0709f109a,0x359ab6419ca1091b,
0xf867241c8cc6d4c0,0xc30163d203c94b62,
0x9b407691d7fc44f8,0x79e0de63425dcf1d,
0xc21094364dfb5636,0x985915fc12f542e4,
0xf294b943e17a2bc4,0x3e6f5b7b17b2939d,
0x979cf3ca6cec5b5a,0xa705992ceecf9c42,
0xbd8430bd08277231,0x50c6ff782a838353,
0xece53cec4a314ebd,0xa4f8bf5635246428,
0x940f4613ae5ed136,0x871b7795e136be99,
0xb913179899f68584,0x28e2557b59846e3f,
0xe757dd7ec07426e5,0x331aeada2fe589cf,
0x9096ea6f3848984f,0x3ff0d2c85def7621,
0xb4bca50b065abe63,0xfed077a756b53a9,
0xe1ebce4dc7f16dfb,0xd3e8495912c62894,
0x8d3360f09cf6e4bd,0x64712dd7abbbd95c,
0xb080392cc4349dec,0xbd8d794d96aacfb3,
0xdca04777f541c567,0xecf0d7a0fc5583a0,
0x89e42caaf9491b60,0xf41686c49db57244,
0xac5d37d5b79b6239,0x311c2875c522ced5,
0xd77485cb25823ac7,0x7d633293366b828b,
0x86a8d39ef77164bc,0xae5dff9c02033197,
0xa8530886b54dbdeb,0xd9f57f830283fdfc,
0xd267caa862a12d66,0xd072df63c324fd7b,
0x8380dea93da4bc60,0x4247cb9e59f71e6d,
0xa46116538d0deb78,0x52d9be85f074e608,
0xcd795be870516656,0x67902e276c921f8b,
0x806bd9714632dff6,0xba1cd8a3db53b6,
0xa086cfcd97bf97f3,0x80e8a40eccd228a4,
0xc8a883c0fdaf7df0,0x6122cd128006b2cd,
0xfad2a4b13d1b5d6c,0x796b805720085f81,
0x9cc3a6eec6311a63,0xcbe3303674053bb0,
0xc3f490aa77bd60fc,0xbedbfc4411068a9c,
0xf4f1b4d515acb93b,0xee92fb5515482d44,
0x991711052d8bf3c5,0x751bdd152d4d1c4a,
0xbf5cd54678eef0b6,0xd262d45a78a0635d,
0xef340a98172aace4,0x86fb897116c87c34,
0x9580869f0e7aac0e,0xd45d35e6ae3d4da0,
0xbae0a846d2195712,0x8974836059cca109,
0xe998d258869facd7,0x2bd1a438703fc94b,
0x91ff83775423cc06,0x7b6306a34627ddcf,
0xb67f6455292cbf08,0x1a3bc84c17b1d542,
0xe41f3d6a7377eeca,0x20caba5f1d9e4a93,
0x8e938662882af53e,0x547eb47b7282ee9c,
0xb23867fb2a35b28d,0xe99e619a4f23aa43,
0xdec681f9f4c31f31,0x6405fa00e2ec94d4,
0x8b3c113c38f9f37e,0xde83bc408dd3dd04,
0xae0b158b4738705e,0x9624ab50b148d445,
0xd98ddaee19068c76,0x3badd624dd9b0957,
0x87f8a8d4cfa417c9,0xe54ca5d70a80e5d6,
0xa9f6d30a038d1dbc,0x5e9fcf4ccd211f4c,
0xd47487cc8470652b,0x7647c3200069671f,
0x84c8d4dfd2c63f3b,0x29ecd9f40041e073,
0xa5fb0a17c777cf09,0xf468107100525890,
0xcf79cc9db955c2cc,0x7182148d4066eeb4,
0x81ac1fe293d599bf,0xc6f14cd848405530,
0xa21727db38cb002f,0xb8ada00e5a506a7c,
0xca9cf1d206fdc03b,0xa6d90811f0e4851c,
0xfd442e4688bd304a,0x908f4a166d1da663,
0x9e4a9cec15763e2e,0x9a598e4e043287fe,
0xc5dd44271ad3cdba,0x40eff1e1853f29fd,
0xf7549530e188c128,0xd12bee59e68ef47c,
0x9a94dd3e8cf578b9,0x82bb74f8301958ce,
0xc13a148e3032d6e7,0xe36a52363c1faf01,
0xf18899b1bc3f8ca1,0xdc44e6c3cb279ac1,
0x96f5600f15a7b7e5,0x29ab103a5ef8c0b9,
0xbcb2b812db11a5de,0x7415d448f6b6f0e7,
0xebdf661791d60f56,0x111b495b3464ad21,
0x936b9fcebb25c995,0xcab10dd900beec34,
0xb84687c269ef3bfb,0x3d5d514f40eea742,
0xe65829b3046b0afa,0xcb4a5a3112a5112,
0x8ff71a0fe2c2e6dc,0x47f0e785eaba72ab,
0xb3f4e093db73a093,0x59ed216765690f56,
0xe0f218b8d25088b8,0x306869c13ec3532c,
0x8c974f7383725573,0x1e414218c73a13fb,
0xafbd2350644eeacf,0xe5d1929ef90898fa,
0xdbac6c247d62a583,0xdf45f746b74abf39,
0x894bc396ce5da772,0x6b8bba8c328eb783,
0xab9eb47c81f5114f,0x66ea92f3f326564,
0xd686619ba27255a2,0xc80a537b0efefebd,
0x8613fd0145877585,0xbd06742ce95f5f36,
0xa798fc4196e952e7,0x2c48113823b73704,
0xd17f3b51fca3a7a0,0xf75a15862ca504c5,
0x82ef85133de648c4,0x9a984d73dbe722fb,
0xa3ab66580d5fdaf5,0xc13e60d0d2e0ebba,
0xcc963fee10b7d1b3,0x318df905079926a8,
0xffbbcfe994e5c61f,0xfdf17746497f7052,
0x9fd561f1fd0f9bd3,0xfeb6ea8bedefa633,
0xc7caba6e7c5382c8,0xfe64a52ee96b8fc0,
0xf9bd690a1b68637b,0x3dfdce7aa3c673b0,
0x9c1661a651213e2d,0x6bea10ca65c084e,
0xc31bfa0fe5698db8,0x486e494fcff30a62,
0xf3e2f893dec3f126,0x5a89dba3c3efccfa,
0x986ddb5c6b3a76b7,0xf89629465a75e01c,
0xbe89523386091465,0xf6bbb397f1135823,
0xee2ba6c0678b597f,0x746aa07ded582e2c,
0x94db483840b717ef,0xa8c2a44eb4571cdc,
0xba121a4650e4ddeb,0x92f34d62616ce413,
0xe896a0d7e51e1566,0x77b020baf9c81d17,
0x915e2486ef32cd60,0xace1474dc1d122e,
0xb5b5ada8aaff80b8,0xd819992132456ba,
0xe3231912d5bf60e6,0x10e1fff697ed6c69,
0x8df5efabc5979c8f,0xca8d3ffa1ef463c1,
0xb1736b96b6fd83b3,0xbd308ff8a6b17cb2,
0xddd0467c64bce4a0,0xac7cb3f6d05ddbde,
0x8aa22c0dbef60ee4,0x6bcdf07a423aa96b,
0xad4ab7112eb3929d,0x86c16c98d2c953c6,
0xd89d64d57a607744,0xe871c7bf077ba8b7,
0x87625f056c7c4a8b,0x11471cd764ad4972,
0xa93af6c6c79b5d2d,0xd598e40d3dd89bcf,
0xd389b47879823479,0x4aff1d108d4ec2c3,
0x843610cb4bf160cb,0xcedf722a585139ba,
0xa54394fe1eedb8fe,0xc2974eb4ee658828,
0xce947a3da6a9273e,0x733d226229feea32,
0x811ccc668829b887,0x806357d5a3f525f,
0xa163ff802a3426a8,0xca07c2dcb0cf26f7,
0xc9bcff6034c13052,0xfc89b393dd02f0b5,
0xfc2c3f3841f17c67,0xbbac2078d443ace2,
0x9d9ba7832936edc0,0xd54b944b84aa4c0d,
0xc5029163f384a931,0xa9e795e65d4df11,
0xf64335bcf065d37d,0x4d4617b5ff4a16d5,
0x99ea0196163fa42e,0x504bced1bf8e4e45,
0xc06481fb9bcf8d39,0xe45ec2862f71e1d6,
0xf07da27a82c37088,0x5d767327bb4e5a4c,
0x964e858c91ba2655,0x3a6a07f8d510f86f,
0xbbe226efb628afea,0x890489f70a55368b,
0xeadab0aba3b2dbe5,0x2b45ac74ccea842e,
0x92c8ae6b464fc96f,0x3b0b8bc90012929d,
0xb77ada0617e3bbcb,0x9ce6ebb40173744,
0xe55990879ddcaabd,0xcc420a6a101d0515,
0x8f57fa54c2a9eab6,0x9fa946824a12232d,
0xb32df8e9f3546564,0x47939822dc96abf9,
0xdff9772470297ebd,0x59787e2b93bc56f7,
0x8bfbea76c619ef36,0x57eb4edb3c55b65a,
0xaefae51477a06b03,0xede622920b6b23f1,
0xdab99e59958885c4,0xe95fab368e45eced,
0x88b402f7fd75539b,0x11dbcb0218ebb414,
0xaae103b5fcd2a881,0xd652bdc29f26a119,
0xd59944a37c0752a2,0x4be76d3346f0495f,
0x857fcae62d8493a5,0x6f70a4400c562ddb,
0xa6dfbd9fb8e5b88e,0xcb4ccd500f6bb952,
0xd097ad07a71f26b2,0x7e2000a41346a7a7,
0x825ecc24c873782f,0x8ed400668c0c28c8,
0xa2f67f2dfa90563b,0x728900802f0f32fa,
0xcbb41ef979346bca,0x4f2b40a03ad2ffb9,
0xfea126b7d78186bc,0xe2f610c84987bfa8,
0x9f24b832e6b0f436,0xdd9ca7d2df4d7c9,
0xc6ede63fa05d3143,0x91503d1c79720dbb,
0xf8a95fcf88747d94,0x75a44c6397ce912a,
0x9b69dbe1b548ce7c,0xc986afbe3ee11aba,
0xc24452da229b021b,0xfbe85badce996168,
0xf2d56790ab41c2a2,0xfae27299423fb9c3,
0x97c560ba6b0919a5,0xdccd879fc967d41a,
0xbdb6b8e905cb600f,0x5400e987bbc1c920,
0xed246723473e3813,0x290123e9aab23b68,
0x9436c0760c86e30b,0xf9a0b6720aaf6521,
0xb94470938fa89bce,0xf808e40e8d5b3e69,
0xe7958cb87392c2c2,0xb60b1d1230b20e04,
0x90bd77f3483bb9b9,0xb1c6f22b5e6f48c2,
0xb4ecd5f01a4aa828,0x1e38aeb6360b1af3,
0xe2280b6c20dd5232,0x25c6da63c38de1b0,
0x8d590723948a535f,0x579c487e5a38ad0e,
0xb0af48ec79ace837,0x2d835a9df0c6d851,
0xdcdb1b2798182244,0xf8e431456cf88e65,
0x8a08f0f8bf0f156b,0x1b8e9ecb641b58ff,
0xac8b2d36eed2dac5,0xe272467e3d222f3f,
0xd7adf884aa879177,0x5b0ed81dcc6abb0f,
0x86ccbb52ea94baea,0x98e947129fc2b4e9,
0xa87fea27a539e9a5,0x3f2398d747b36224,
0xd29fe4b18e88640e,0x8eec7f0d19a03aad,
0x83a3eeeef9153e89,0x1953cf68300424ac,
0xa48ceaaab75a8e2b,0x5fa8c3423c052dd7,
0xcdb02555653131b6,0x3792f412cb06794d,
0x808e17555f3ebf11,0xe2bbd88bbee40bd0,
0xa0b19d2ab70e6ed6,0x5b6aceaeae9d0ec4,
0xc8de047564d20a8b,0xf245825a5a445275,
0xfb158592be068d2e,0xeed6e2f0f0d56712,
0x9ced737bb6c4183d,0x55464dd69685606b,
0xc428d05aa4751e4c,0xaa97e14c3c26b886,
0xf53304714d9265df,0xd53dd99f4b3066a8,
0x993fe2c6d07b7fab,0xe546a8038efe4029,
0xbf8fdb78849a5f96,0xde98520472bdd033,
0xef73d256a5c0f77c,0x963e66858f6d4440,
0x95a8637627989aad,0xdde7001379a44aa8,
0xbb127c53b17ec159,0x5560c018580d5d52,
0xe9d71b689dde71af,0xaab8f01e6e10b4a6,
0x9226712162ab070d,0xcab3961304ca70e8,
0xb6b00d69bb55c8d1,0x3d607b97c5fd0d22,
0xe45c10c42a2b3b05,0x8cb89a7db77c506a,
0x8eb98a7a9a5b04e3,0x77f3608e92adb242,
0xb267ed1940f1c61c,0x55f038b237591ed3,
0xdf01e85f912e37a3,0x6b6c46dec52f6688,
0x8b61313bbabce2c6,0x2323ac4b3b3da015,
0xae397d8aa96c1b77,0xabec975e0a0d081a,
0xd9c7dced53c72255,0x96e7bd358c904a21,
0x881cea14545c7575,0x7e50d64177da2e54,
0xaa242499697392d2,0xdde50bd1d5d0b9e9,
0xd4ad2dbfc3d07787,0x955e4ec64b44e864,
0x84ec3c97da624ab4,0xbd5af13bef0b113e,
0xa6274bbdd0fadd61,0xecb1ad8aeacdd58e,
0xcfb11ead453994ba,0x67de18eda5814af2,
0x81ceb32c4b43fcf4,0x80eacf948770ced7,
0xa2425ff75e14fc31,0xa1258379a94d028d,
0xcad2f7f5359a3b3e,0x96ee45813a04330,
0xfd87b5f28300ca0d,0x8bca9d6e188853fc,
0x9e74d1b791e07e48,0x775ea264cf55347e,
0xc612062576589dda,0x95364afe032a819e,
0xf79687aed3eec551,0x3a83ddbd83f52205,
0x9abe14cd44753b52,0xc4926a9672793543,
0xc16d9a0095928a27,0x75b7053c0f178294,
0xf1c90080baf72cb1,0x5324c68b12dd6339,
0x971da05074da7bee,0xd3f6fc16ebca5e04,
0xbce5086492111aea,0x88f4bb1ca6bcf585,
0xec1e4a7db69561a5,0x2b31e9e3d06c32e6,
0x9392ee8e921d5d07,0x3aff322e62439fd0,
0xb877aa3236a4b449,0x9befeb9fad487c3,
0xe69594bec44de15b,0x4c2ebe687989a9b4,
0x901d7cf73ab0acd9,0xf9d37014bf60a11,
0xb424dc35095cd80f,0x538484c19ef38c95,
0xe12e13424bb40e13,0x2865a5f206b06fba,
0x8cbccc096f5088cb,0xf93f87b7442e45d4,
0xafebff0bcb24aafe,0xf78f69a51539d749,
0xdbe6fecebdedd5be,0xb573440e5a884d1c,
0x89705f4136b4a597,0x31680a88f8953031,
0xabcc77118461cefc,0xfdc20d2b36ba7c3e,
0xd6bf94d5e57a42bc,0x3d32907604691b4d,
0x8637bd05af6c69b5,0xa63f9a49c2c1b110,
0xa7c5ac471b478423,0xfcf80dc33721d54,
0xd1b71758e219652b,0xd3c36113404ea4a9,
0x83126e978d4fdf3b,0x645a1cac083126ea,
0xa3d70a3d70a3d70a,0x3d70a3d70a3d70a4,
0xcccccccccccccccc,0xcccccccccccccccd,
0x8000000000000000,0x0,
0xa000000000000000,0x0,
0xc800000000000000,0x0,
0xfa00000000000000,0x0,
0x9c40000000000000,0x0,
0xc350000000000000,0x0,
0xf424000000000000,0x0,
0x9896800000000000,0x0,
0xbebc200000000000,0x0,
0xee6b280000000000,0x0,
0x9502f90000000000,0x0,
0xba43b74000000000,0x0,
0xe8d4a51000000000,0x0,
0x9184e72a00000000,0x0,
0xb5e620f480000000,0x0,
0xe35fa931a0000000,0x0,
0x8e1bc9bf04000000,0x0,
0xb1a2bc2ec5000000,0x0,
0xde0b6b3a76400000,0x0,
0x8ac7230489e80000,0x0,
0xad78ebc5ac620000,0x0,
0xd8d726b7177a8000,0x0,
0x878678326eac9000,0x0,
0xa968163f0a57b400,0x0,
0xd3c21bcecceda100,0x0,
0x84595161401484a0,0x0,
0xa56fa5b99019a5c8,0x0,
0xcecb8f27f4200f3a,0x0,
0x813f3978f8940984,0x4000000000000000,
0xa18f07d736b90be5,0x5000000000000000,
0xc9f2c9cd04674ede,0xa400000000000000,
0xfc6f7c4045812296,0x4d00000000000000,
0x9dc5ada82b70b59d,0xf020000000000000,
0xc5371912364ce305,0x6c28000000000000,
0xf684df56c3e01bc6,0xc732000000000000,
0x9a130b963a6c115c,0x3c7f400000000000,
0xc097ce7bc90715b3,0x4b9f100000000000,
0xf0bdc21abb48db20,0x1e86d40000000000,
0x96769950b50d88f4,0x1314448000000000,
0xbc143fa4e250eb31,0x17d955a000000000,
0xeb194f8e1ae525fd,0x5dcfab0800000000,
0x92efd1b8d0cf37be,0x5aa1cae500000000,
0xb7abc627050305ad,0xf14a3d9e40000000,
0xe596b7b0c643c719,0x6d9ccd05d0000000,
0x8f7e32ce7bea5c6f,0xe4820023a2000000,
0xb35dbf821ae4f38b,0xdda2802c8a800000,
0xe0352f62a19e306e,0xd50b2037ad200000,
0x8c213d9da502de45,0x4526f422cc340000,
0xaf298d050e4395d6,0x9670b12b7f410000,
0xdaf3f04651d47b4c,0x3c0cdd765f114000,
0x88d8762bf324cd0f,0xa5880a69fb6ac800,
0xab0e93b6efee0053,0x8eea0d047a457a00,
0xd5d238a4abe98068,0x72a4904598d6d880,
0x85a36366eb71f041,0x47a6da2b7f864750,
0xa70c3c40a64e6c51,0x999090b65f67d924,
0xd0cf4b50cfe20765,0xfff4b4e3f741cf6d,
0x82818f1281ed449f,0xbff8f10e7a8921a4,
0xa321f2d7226895c7,0xaff72d52192b6a0d,
0xcbea6f8ceb02bb39,0x9bf4f8a69f764490,
0xfee50b7025c36a08,0x2f236d04753d5b4,
0x9f4f2726179a2245,0x1d762422c946590,
0xc722f0ef9d80aad6,0x424d3ad2b7b97ef5,
0xf8ebad2b84e0d58b,0xd2e0898765a7deb2,
0x9b934c3b330c8577,0x63cc55f49f88eb2f,
0xc2781f49ffcfa6d5,0x3cbf6b71c76b25fb,
0xf316271c7fc3908a,0x8bef464e3945ef7a,
0x97edd871cfda3a56,0x97758bf0e3cbb5ac,
0xbde94e8e43d0c8ec,0x3d52eeed1cbea317,
0xed63a231d4c4fb27,0x4ca7aaa863ee4bdd,
0x945e455f24fb1cf8,0x8fe8caa93e74ef6a,
0xb975d6b6ee39e436,0xb3e2fd538e122b44,
0xe7d34c64a9c85d44,0x60dbbca87196b616,
0x90e40fbeea1d3a4a,0xbc8955e946fe31cd,
0xb51d13aea4a488dd,0x6babab6398bdbe41,
0xe264589a4dcdab14,0xc696963c7eed2dd1,
0x8d7eb76070a08aec,0xfc1e1de5cf543ca2,
0xb0de65388cc8ada8,0x3b25a55f43294bcb,
0xdd15fe86affad912,0x49ef0eb713f39ebe,
0x8a2dbf142dfcc7ab,0x6e3569326c784337,
0xacb92ed9397bf996,0x49c2c37f07965404,
0xd7e77a8f87daf7fb,0xdc33745ec97be906,
0x86f0ac99b4e8dafd,0x69a028bb3ded71a3,
0xa8acd7c0222311bc,0xc40832ea0d68ce0c,
0xd2d80db02aabd62b,0xf50a3fa490c30190,
0x83c7088e1aab65db,0x792667c6da79e0fa,
0xa4b8cab1a1563f52,0x577001b891185938,
0xcde6fd5e09abcf26,0xed4c0226b55e6f86,
0x80b05e5ac60b6178,0x544f8158315b05b4,
0xa0dc75f1778e39d6,0x696361ae3db1c721,
0xc913936dd571c84c,0x3bc3a19cd1e38e9,
0xfb5878494ace3a5f,0x4ab48a04065c723,
0x9d174b2dcec0e47b,0x62eb0d64283f9c76,
0xc45d1df942711d9a,0x3ba5d0bd324f8394,
0xf5746577930d6500,0xca8f44ec7ee36479,
0x9968bf6abbe85f20,0x7e998b13cf4e1ecb,
0xbfc2ef456ae276e8,0x9e3fedd8c321a67e,
0xefb3ab16c59b14a2,0xc5cfe94ef3ea101e,
0x95d04aee3b80ece5,0xbba1f1d158724a12,
0xbb445da9ca61281f,0x2a8a6e45ae8edc97,
0xea1575143cf97226,0xf52d09d71a3293bd,
0x924d692ca61be758,0x593c2626705f9c56,
0xb6e0c377cfa2e12e,0x6f8b2fb00c77836c,
0xe498f455c38b997a,0xb6dfb9c0f956447,
0x8edf98b59a373fec,0x4724bd4189bd5eac,
0xb2977ee300c50fe7,0x58edec91ec2cb657,
0xdf3d5e9bc0f653e1,0x2f2967b66737e3ed,
0x8b865b215899f46c,0xbd79e0d20082ee74,
0xae67f1e9aec07187,0xecd8590680a3aa11,
0xda01ee641a708de9,0xe80e6f4820cc9495,
0x884134fe908658b2,0x3109058d147fdcdd,
0xaa51823e34a7eede,0xbd4b46f0599fd415,
0xd4e5e2cdc1d1ea96,0x6c9e18ac7007c91a,
0x850fadc09923329e,0x3e2cf6bc604ddb0,
0xa6539930bf6bff45,0x84db8346b786151c,
0xcfe87f7cef46ff16,0xe612641865679a63,
0x81f14fae158c5f6e,0x4fcb7e8f3f60c07e,
0xa26da3999aef7749,0xe3be5e330f38f09d,
0xcb090c8001ab551c,0x5cadf5bfd3072cc5,
0xfdcb4fa002162a63,0x73d9732fc7c8f7f6,
0x9e9f11c4014dda7e,0x2867e7fddcdd9afa,
0xc646d63501a1511d,0xb281e1fd541501b8,
0xf7d88bc24209a565,0x1f225a7ca91a4226,
0x9ae757596946075f,0x3375788de9b06958,
0xc1a12d2fc3978937,0x52d6b1641c83ae,
0xf209787bb47d6b84,0xc0678c5dbd23a49a,
0x9745eb4d50ce6332,0xf840b7ba963646e0,
0xbd176620a501fbff,0xb650e5a93bc3d898,
0xec5d3fa8ce427aff,0xa3e51f138ab4cebe,
0x93ba47c980e98cdf,0xc66f336c36b10137,
0xb8a8d9bbe123f017,0xb80b0047445d4184,
0xe6d3102ad96cec1d,0xa60dc059157491e5,
0x9043ea1ac7e41392,0x87c89837ad68db2f,
0xb454e4a179dd1877,0x29babe4598c311fb,
0xe16a1dc9d8545e94,0xf4296dd6fef3d67a,
0x8ce2529e2734bb1d,0x1899e4a65f58660c,
0xb01ae745b101e9e4,0x5ec05dcff72e7f8f,
0xdc21a1171d42645d,0x76707543f4fa1f73,
0x899504ae72497eba,0x6a06494a791c53a8,
0xabfa45da0edbde69,0x487db9d17636892,
0xd6f8d7509292d603,0x45a9d2845d3c42b6,
0x865b86925b9bc5c2,0xb8a2392ba45a9b2,
0xa7f26836f282b732,0x8e6cac7768d7141e,
0xd1ef0244af2364ff,0x3207d795430cd926,
0x8335616aed761f1f,0x7f44e6bd49e807b8,
0xa402b9c5a8d3a6e7,0x5f16206c9c6209a6,
0xcd036837130890a1,0x36dba887c37a8c0f,
0x802221226be55a64,0xc2494954da2c9789,
0xa02aa96b06deb0fd,0xf2db9baa10b7bd6c,
0xc83553c5c8965d3d,0x6f92829494e5acc7,
0xfa42a8b73abbf48c,0xcb772339ba1f17f9,
0x9c69a97284b578d7,0xff2a760414536efb,
0xc38413cf25e2d70d,0xfef5138519684aba,
0xf46518c2ef5b8cd1,0x7eb258665fc25d69,
0x98bf2f79d5993802,0xef2f773ffbd97a61,
0xbeeefb584aff8603,0xaafb550ffacfd8fa,
0xeeaaba2e5dbf6784,0x95ba2a53f983cf38,
0x952ab45cfa97a0b2,0xdd945a747bf26183,
0xba756174393d88df,0x94f971119aeef9e4,
0xe912b9d1478ceb17,0x7a37cd5601aab85d,
0x91abb422ccb812ee,0xac62e055c10ab33a,
0xb616a12b7fe617aa,0x577b986b314d6009,
0xe39c49765fdf9d94,0xed5a7e85fda0b80b,
0x8e41ade9fbebc27d,0x14588f13be847307,
0xb1d219647ae6b31c,0x596eb2d8ae258fc8,
0xde469fbd99a05fe3,0x6fca5f8ed9aef3bb,
0x8aec23d680043bee,0x25de7bb9480d5854,
0xada72ccc20054ae9,0xaf561aa79a10ae6a,
0xd910f7ff28069da4,0x1b2ba1518094da04,
0x87aa9aff79042286,0x90fb44d2f05d0842,
0xa99541bf57452b28,0x353a1607ac744a53,
0xd3fa922f2d1675f2,0x42889b8997915ce8,
0x847c9b5d7c2e09b7,0x69956135febada11,
0xa59bc234db398c25,0x43fab9837e699095,
0xcf02b2c21207ef2e,0x94f967e45e03f4bb,
0x8161afb94b44f57d,0x1d1be0eebac278f5,
0xa1ba1ba79e1632dc,0x6462d92a69731732,
0xca28a291859bbf93,0x7d7b8f7503cfdcfe,
0xfcb2cb35e702af78,0x5cda735244c3d43e,
0x9defbf01b061adab,0x3a0888136afa64a7,
0xc56baec21c7a1916,0x88aaa1845b8fdd0,
0xf6c69a72a3989f5b,0x8aad549e57273d45,
0x9a3c2087a63f6399,0x36ac54e2f678864b,
0xc0cb28a98fcf3c7f,0x84576a1bb416a7dd,
0xf0fdf2d3f3c30b9f,0x656d44a2a11c51d5,
0x969eb7c47859e743,0x9f644ae5a4b1b325,
0xbc4665b596706114,0x873d5d9f0dde1fee,
0xeb57ff22fc0c7959,0xa90cb506d155a7ea,
0x9316ff75dd87cbd8,0x9a7f12442d588f2,
0xb7dcbf5354e9bece,0xc11ed6d538aeb2f,
0xe5d3ef282a242e81,0x8f1668c8a86da5fa,
0x8fa475791a569d10,0xf96e017d694487bc,
0xb38d92d760ec4455,0x37c981dcc395a9ac,
0xe070f78d3927556a,0x85bbe253f47b1417,
0x8c469ab843b89562,0x93956d7478ccec8e,
0xaf58416654a6babb,0x387ac8d1970027b2,
0xdb2e51bfe9d0696a,0x6997b05fcc0319e,
0x88fcf317f22241e2,0x441fece3bdf81f03,
0xab3c2fddeeaad25a,0xd527e81cad7626c3,
0xd60b3bd56a5586f1,0x8a71e223d8d3b074,
0x85c7056562757456,0xf6872d5667844e49,
0xa738c6bebb12d16c,0xb428f8ac016561db,
0xd106f86e69d785c7,0xe13336d701beba52,
0x82a45b450226b39c,0xecc0024661173473,
0xa34d721642b06084,0x27f002d7f95d0190,
0xcc20ce9bd35c78a5,0x31ec038df7b441f4,
0xff290242c83396ce,0x7e67047175a15271,
0x9f79a169bd203e41,0xf0062c6e984d386,
0xc75809c42c684dd1,0x52c07b78a3e60868,
0xf92e0c3537826145,0xa7709a56ccdf8a82,
0x9bbcc7a142b17ccb,0x88a66076400bb691,
0xc2abf989935ddbfe,0x6acff893d00ea435,
0xf356f7ebf83552fe,0x583f6b8c4124d43,
0x98165af37b2153de,0xc3727a337a8b704a,
0xbe1bf1b059e9a8d6,0x744f18c0592e4c5c,
0xeda2ee1c7064130c,0x1162def06f79df73,
0x9485d4d1c63e8be7,0x8addcb5645ac2ba8,
0xb9a74a0637ce2ee1,0x6d953e2bd7173692,
0xe8111c87c5c1ba99,0xc8fa8db6ccdd0437,
0x910ab1d4db9914a0,0x1d9c9892400a22a2,
0xb54d5e4a127f59c8,0x2503beb6d00cab4b,
0xe2a0b5dc971f303a,0x2e44ae64840fd61d,
0x8da471a9de737e24,0x5ceaecfed289e5d2,
0xb10d8e1456105dad,0x7425a83e872c5f47,
0xdd50f1996b947518,0xd12f124e28f77719,
0x8a5296ffe33cc92f,0x82bd6b70d99aaa6f,
0xace73cbfdc0bfb7b,0x636cc64d1001550b,
0xd8210befd30efa5a,0x3c47f7e05401aa4e,
0x8714a775e3e95c78,0x65acfaec34810a71,
0xa8d9d1535ce3b396,0x7f1839a741a14d0d,
0xd31045a8341ca07c,0x1ede48111209a050,
0x83ea2b892091e44d,0x934aed0aab460432,
0xa4e4b66b68b65d60,0xf81da84d5617853f,
0xce1de40642e3f4b9,0x36251260ab9d668e,
0x80d2ae83e9ce78f3,0xc1d72b7c6b426019,
0xa1075a24e4421730,0xb24cf65b8612f81f,
0xc94930ae1d529cfc,0xdee033f26797b627,
0xfb9b7cd9a4a7443c,0x169840ef017da3b1,
0x9d412e0806e88aa5,0x8e1f289560ee864e,
0xc491798a08a2ad4e,0xf1a6f2bab92a27e2,
0xf5b5d7ec8acb58a2,0xae10af696774b1db,
0x9991a6f3d6bf1765,0xacca6da1e0a8ef29,
0xbff610b0cc6edd3f,0x17fd090a58d32af3,
0xeff394dcff8a948e,0xddfc4b4cef07f5b0,
0x95f83d0a1fb69cd9,0x4abdaf101564f98e,
0xbb764c4ca7a4440f,0x9d6d1ad41abe37f1,
0xea53df5fd18d5513,0x84c86189216dc5ed,
0x92746b9be2f8552c,0x32fd3cf5b4e49bb4,
0xb7118682dbb66a77,0x3fbc8c33221dc2a1,
0xe4d5e82392a40515,0xfabaf3feaa5334a,
0x8f05b1163ba6832d,0x29cb4d87f2a7400e,
0xb2c71d5bca9023f8,0x743e20e9ef511012,
0xdf78e4b2bd342cf6,0x914da9246b255416,
0x8bab8eefb6409c1a,0x1ad089b6c2f7548e,
0xae9672aba3d0c320,0xa184ac2473b529b1,
0xda3c0f568cc4f3e8,0xc9e5d72d90a2741e,
0x8865899617fb1871,0x7e2fa67c7a658892,
0xaa7eebfb9df9de8d,0xddbb901b98feeab7,
0xd51ea6fa85785631,0x552a74227f3ea565,
0x8533285c936b35de,0xd53a88958f87275f,
0xa67ff273b8460356,0x8a892abaf368f137,
0xd01fef10a657842c,0x2d2b7569b0432d85,
0x8213f56a67f6b29b,0x9c3b29620e29fc73,
0xa298f2c501f45f42,0x8349f3ba91b47b8f,
0xcb3f2f7642717713,0x241c70a936219a73,
0xfe0efb53d30dd4d7,0xed238cd383aa0110,
0x9ec95d1463e8a506,0xf4363804324a40aa,
0xc67bb4597ce2ce48,0xb143c6053edcd0d5,
0xf81aa16fdc1b81da,0xdd94b7868e94050a,
0x9b10a4e5e9913128,0xca7cf2b4191c8326,
0xc1d4ce1f63f57d72,0xfd1c2f611f63a3f0,
0xf24a01a73cf2dccf,0xbc633b39673c8cec,
0x976e41088617ca01,0xd5be0503e085d813,
0xbd49d14aa79dbc82,0x4b2d8644d8a74e18,
0xec9c459d51852ba2,0xddf8e7d60ed1219e,
0x93e1ab8252f33b45,0xcabb90e5c942b503,
0xb8da1662e7b00a17,0x3d6a751f3b936243,
0xe7109bfba19c0c9d,0xcc512670a783ad4,
0x906a617d450187e2,0x27fb2b80668b24c5,
0xb484f9dc9641e9da,0xb1f9f660802dedf6,
0xe1a63853bbd26451,0x5e7873f8a0396973,
0x8d07e33455637eb2,0xdb0b487b6423e1e8,
0xb049dc016abc5e5f,0x91ce1a9a3d2cda62,
0xdc5c5301c56b75f7,0x7641a140cc7810fb,
0x89b9b3e11b6329ba,0xa9e904c87fcb0a9d,
0xac2820d9623bf429,0x546345fa9fbdcd44,
0xd732290fbacaf133,0xa97c177947ad4095,
0x867f59a9d4bed6c0,0x49ed8eabcccc485d,
0xa81f301449ee8c70,0x5c68f256bfff5a74,
0xd226fc195c6a2f8c,0x73832eec6fff3111,
0x83585d8fd9c25db7,0xc831fd53c5ff7eab,
0xa42e74f3d032f525,0xba3e7ca8b77f5e55,
0xcd3a1230c43fb26f,0x28ce1bd2e55f35eb,
0x80444b5e7aa7cf85,0x7980d163cf5b81b3,
0xa0555e361951c366,0xd7e105bcc332621f,
0xc86ab5c39fa63440,0x8dd9472bf3fefaa7,
0xfa856334878fc150,0xb14f98f6f0feb951,
0x9c935e00d4b9d8d2,0x6ed1bf9a569f33d3,
0xc3b8358109e84f07,0xa862f80ec4700c8,
0xf4a642e14c6262c8,0xcd27bb612758c0fa,
0x98e7e9cccfbd7dbd,0x8038d51cb897789c,
0xbf21e44003acdd2c,0xe0470a63e6bd56c3,
0xeeea5d5004981478,0x1858ccfce06cac74,
0x95527a5202df0ccb,0xf37801e0c43ebc8,
0xbaa718e68396cffd,0xd30560258f54e6ba,
0xe950df20247c83fd,0x47c6b82ef32a2069,
0x91d28b7416cdd27e,0x4cdc331d57fa5441,
0xb6472e511c81471d,0xe0133fe4adf8e952,
0xe3d8f9e563a198e5,0x58180fddd97723a6,
0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,};
};
template <class unused>
constexpr uint64_t powers_template<unused>::power_of_five_128[number_of_entries];
using powers = powers_template<>;
} // namespace fast_float
#endif

View File

@ -0,0 +1,767 @@
#ifndef FASTFLOAT_FLOAT_COMMON_H
#define FASTFLOAT_FLOAT_COMMON_H
#include <cfloat>
#include <cstdint>
#include <cassert>
#include <cstring>
#include <type_traits>
#include <system_error>
#ifdef __has_include
#if __has_include(<stdfloat>) && (__cplusplus > 202002L || _MSVC_LANG > 202002L)
#include <stdfloat>
#endif
#endif
#include "constexpr_feature_detect.h"
namespace fast_float {
#define FASTFLOAT_JSONFMT (1 << 5)
#define FASTFLOAT_FORTRANFMT (1 << 6)
enum chars_format {
scientific = 1 << 0,
fixed = 1 << 2,
hex = 1 << 3,
no_infnan = 1 << 4,
// RFC 8259: https://datatracker.ietf.org/doc/html/rfc8259#section-6
json = FASTFLOAT_JSONFMT | fixed | scientific | no_infnan,
// Extension of RFC 8259 where, e.g., "inf" and "nan" are allowed.
json_or_infnan = FASTFLOAT_JSONFMT | fixed | scientific,
fortran = FASTFLOAT_FORTRANFMT | fixed | scientific,
general = fixed | scientific
};
template <typename UC>
struct from_chars_result_t {
UC const* ptr;
std::errc ec;
};
using from_chars_result = from_chars_result_t<char>;
template <typename UC>
struct parse_options_t {
constexpr explicit parse_options_t(chars_format fmt = chars_format::general,
UC dot = UC('.'))
: format(fmt), decimal_point(dot) {}
/** Which number formats are accepted */
chars_format format;
/** The character used as decimal point */
UC decimal_point;
};
using parse_options = parse_options_t<char>;
}
#if FASTFLOAT_HAS_BIT_CAST
#include <bit>
#endif
#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \
|| defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \
|| defined(__MINGW64__) \
|| defined(__s390x__) \
|| (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) \
|| defined(__loongarch64) )
#define FASTFLOAT_64BIT 1
#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \
|| defined(__arm__) || defined(_M_ARM) || defined(__ppc__) \
|| defined(__MINGW32__) || defined(__EMSCRIPTEN__))
#define FASTFLOAT_32BIT 1
#else
// Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow.
// We can never tell the register width, but the SIZE_MAX is a good approximation.
// UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max portability.
#if SIZE_MAX == 0xffff
#error Unknown platform (16-bit, unsupported)
#elif SIZE_MAX == 0xffffffff
#define FASTFLOAT_32BIT 1
#elif SIZE_MAX == 0xffffffffffffffff
#define FASTFLOAT_64BIT 1
#else
#error Unknown platform (not 32-bit, not 64-bit?)
#endif
#endif
#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__))
#include <intrin.h>
#endif
#if defined(_MSC_VER) && !defined(__clang__)
#define FASTFLOAT_VISUAL_STUDIO 1
#endif
#if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__
#define FASTFLOAT_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#elif defined _WIN32
#define FASTFLOAT_IS_BIG_ENDIAN 0
#else
#if defined(__APPLE__) || defined(__FreeBSD__)
#include <machine/endian.h>
#elif defined(sun) || defined(__sun)
#include <sys/byteorder.h>
#elif defined(__MVS__)
#include <sys/endian.h>
#else
#ifdef __has_include
#if __has_include(<endian.h>)
#include <endian.h>
#endif //__has_include(<endian.h>)
#endif //__has_include
#endif
#
#ifndef __BYTE_ORDER__
// safe choice
#define FASTFLOAT_IS_BIG_ENDIAN 0
#endif
#
#ifndef __ORDER_LITTLE_ENDIAN__
// safe choice
#define FASTFLOAT_IS_BIG_ENDIAN 0
#endif
#
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define FASTFLOAT_IS_BIG_ENDIAN 0
#else
#define FASTFLOAT_IS_BIG_ENDIAN 1
#endif
#endif
#if defined(__SSE2__) || \
(defined(FASTFLOAT_VISUAL_STUDIO) && \
(defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP == 2)))
#define FASTFLOAT_SSE2 1
#endif
#if defined(__aarch64__) || defined(_M_ARM64)
#define FASTFLOAT_NEON 1
#endif
#if defined(FASTFLOAT_SSE2) || defined(FASTFLOAT_NEON)
#define FASTFLOAT_HAS_SIMD 1
#endif
#if defined(__GNUC__)
// disable -Wcast-align=strict (GCC only)
#define FASTFLOAT_SIMD_DISABLE_WARNINGS \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wcast-align\"")
#else
#define FASTFLOAT_SIMD_DISABLE_WARNINGS
#endif
#if defined(__GNUC__)
#define FASTFLOAT_SIMD_RESTORE_WARNINGS \
_Pragma("GCC diagnostic pop")
#else
#define FASTFLOAT_SIMD_RESTORE_WARNINGS
#endif
#ifdef FASTFLOAT_VISUAL_STUDIO
#define fastfloat_really_inline __forceinline
#else
#define fastfloat_really_inline inline __attribute__((always_inline))
#endif
#ifndef FASTFLOAT_ASSERT
#define FASTFLOAT_ASSERT(x) { ((void)(x)); }
#endif
#ifndef FASTFLOAT_DEBUG_ASSERT
#define FASTFLOAT_DEBUG_ASSERT(x) { ((void)(x)); }
#endif
// rust style `try!()` macro, or `?` operator
#define FASTFLOAT_TRY(x) { if (!(x)) return false; }
#define FASTFLOAT_ENABLE_IF(...) typename std::enable_if<(__VA_ARGS__), int>::type
namespace fast_float {
fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() {
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED
return std::is_constant_evaluated();
#else
return false;
#endif
}
template <typename T>
fastfloat_really_inline constexpr bool is_supported_float_type() {
return std::is_same<T, float>::value || std::is_same<T, double>::value
#if __STDCPP_FLOAT32_T__
|| std::is_same<T, std::float32_t>::value
#endif
#if __STDCPP_FLOAT64_T__
|| std::is_same<T, std::float64_t>::value
#endif
;
}
template <typename UC>
fastfloat_really_inline constexpr bool is_supported_char_type() {
return
std::is_same<UC, char>::value ||
std::is_same<UC, wchar_t>::value ||
std::is_same<UC, char16_t>::value ||
std::is_same<UC, char32_t>::value;
}
// Compares two ASCII strings in a case insensitive manner.
template <typename UC>
inline FASTFLOAT_CONSTEXPR14 bool
fastfloat_strncasecmp(UC const * input1, UC const * input2, size_t length) {
char running_diff{0};
for (size_t i = 0; i < length; ++i) {
running_diff |= (char(input1[i]) ^ char(input2[i]));
}
return (running_diff == 0) || (running_diff == 32);
}
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
// a pointer and a length to a contiguous block of memory
template <typename T>
struct span {
const T* ptr;
size_t length;
constexpr span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {}
constexpr span() : ptr(nullptr), length(0) {}
constexpr size_t len() const noexcept {
return length;
}
FASTFLOAT_CONSTEXPR14 const T& operator[](size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length);
return ptr[index];
}
};
struct value128 {
uint64_t low;
uint64_t high;
constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
constexpr value128() : low(0), high(0) {}
};
/* Helper C++14 constexpr generic implementation of leading_zeroes */
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
int leading_zeroes_generic(uint64_t input_num, int last_bit = 0) {
if(input_num & uint64_t(0xffffffff00000000)) { input_num >>= 32; last_bit |= 32; }
if(input_num & uint64_t( 0xffff0000)) { input_num >>= 16; last_bit |= 16; }
if(input_num & uint64_t( 0xff00)) { input_num >>= 8; last_bit |= 8; }
if(input_num & uint64_t( 0xf0)) { input_num >>= 4; last_bit |= 4; }
if(input_num & uint64_t( 0xc)) { input_num >>= 2; last_bit |= 2; }
if(input_num & uint64_t( 0x2)) { /* input_num >>= 1; */ last_bit |= 1; }
return 63 - last_bit;
}
/* result might be undefined when input_num is zero */
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
int leading_zeroes(uint64_t input_num) {
assert(input_num > 0);
if (cpp20_and_in_constexpr()) {
return leading_zeroes_generic(input_num);
}
#ifdef FASTFLOAT_VISUAL_STUDIO
#if defined(_M_X64) || defined(_M_ARM64)
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
_BitScanReverse64(&leading_zero, input_num);
return (int)(63 - leading_zero);
#else
return leading_zeroes_generic(input_num);
#endif
#else
return __builtin_clzll(input_num);
#endif
}
// slow emulation routine for 32-bit
fastfloat_really_inline constexpr uint64_t emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y;
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
uint64_t umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) {
uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
uint64_t adbc_carry = (uint64_t)(adbc < ad);
uint64_t lo = bd + (adbc << 32);
*hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
(adbc_carry << 32) + (uint64_t)(lo < bd);
return lo;
}
#ifdef FASTFLOAT_32BIT
// slow emulation routine for 32-bit
#if !defined(__MINGW64__)
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) {
return umul128_generic(ab, cd, hi);
}
#endif // !__MINGW64__
#endif // FASTFLOAT_32BIT
// compute 64-bit a*b
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
value128 full_multiplication(uint64_t a, uint64_t b) {
if (cpp20_and_in_constexpr()) {
value128 answer;
answer.low = umul128_generic(a, b, &answer.high);
return answer;
}
value128 answer;
#if defined(_M_ARM64) && !defined(__MINGW32__)
// ARM64 has native support for 64-bit multiplications, no need to emulate
// But MinGW on ARM64 doesn't have native support for 64-bit multiplications
answer.high = __umulh(a, b);
answer.low = a * b;
#elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__))
answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64
#elif defined(FASTFLOAT_64BIT) && defined(__SIZEOF_INT128__)
__uint128_t r = ((__uint128_t)a) * b;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#else
answer.low = umul128_generic(a, b, &answer.high);
#endif
return answer;
}
struct adjusted_mantissa {
uint64_t mantissa{0};
int32_t power2{0}; // a negative value indicates an invalid result
adjusted_mantissa() = default;
constexpr bool operator==(const adjusted_mantissa &o) const {
return mantissa == o.mantissa && power2 == o.power2;
}
constexpr bool operator!=(const adjusted_mantissa &o) const {
return mantissa != o.mantissa || power2 != o.power2;
}
};
// Bias so we can get the real exponent with an invalid adjusted_mantissa.
constexpr static int32_t invalid_am_bias = -0x8000;
// used for binary_format_lookup_tables<T>::max_mantissa
constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5;
template <typename T, typename U = void>
struct binary_format_lookup_tables;
template <typename T> struct binary_format : binary_format_lookup_tables<T> {
using equiv_uint = typename std::conditional<sizeof(T) == 4, uint32_t, uint64_t>::type;
static inline constexpr int mantissa_explicit_bits();
static inline constexpr int minimum_exponent();
static inline constexpr int infinite_power();
static inline constexpr int sign_index();
static inline constexpr int min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST
static inline constexpr int max_exponent_fast_path();
static inline constexpr int max_exponent_round_to_even();
static inline constexpr int min_exponent_round_to_even();
static inline constexpr uint64_t max_mantissa_fast_path(int64_t power);
static inline constexpr uint64_t max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST
static inline constexpr int largest_power_of_ten();
static inline constexpr int smallest_power_of_ten();
static inline constexpr T exact_power_of_ten(int64_t power);
static inline constexpr size_t max_digits();
static inline constexpr equiv_uint exponent_mask();
static inline constexpr equiv_uint mantissa_mask();
static inline constexpr equiv_uint hidden_bit_mask();
};
template <typename U>
struct binary_format_lookup_tables<double, U> {
static constexpr double powers_of_ten[] = {
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
// Largest integer value v so that (5**index * v) <= 1<<53.
// 0x10000000000000 == 1 << 53
static constexpr uint64_t max_mantissa[] = {
0x10000000000000,
0x10000000000000 / 5,
0x10000000000000 / (5 * 5),
0x10000000000000 / (5 * 5 * 5),
0x10000000000000 / (5 * 5 * 5 * 5),
0x10000000000000 / (constant_55555),
0x10000000000000 / (constant_55555 * 5),
0x10000000000000 / (constant_55555 * 5 * 5),
0x10000000000000 / (constant_55555 * 5 * 5 * 5),
0x10000000000000 / (constant_55555 * 5 * 5 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555),
0x10000000000000 / (constant_55555 * constant_55555 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * 5 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5)};
};
template <typename U>
constexpr double binary_format_lookup_tables<double, U>::powers_of_ten[];
template <typename U>
constexpr uint64_t binary_format_lookup_tables<double, U>::max_mantissa[];
template <typename U>
struct binary_format_lookup_tables<float, U> {
static constexpr float powers_of_ten[] = {1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
// Largest integer value v so that (5**index * v) <= 1<<24.
// 0x1000000 == 1<<24
static constexpr uint64_t max_mantissa[] = {
0x1000000,
0x1000000 / 5,
0x1000000 / (5 * 5),
0x1000000 / (5 * 5 * 5),
0x1000000 / (5 * 5 * 5 * 5),
0x1000000 / (constant_55555),
0x1000000 / (constant_55555 * 5),
0x1000000 / (constant_55555 * 5 * 5),
0x1000000 / (constant_55555 * 5 * 5 * 5),
0x1000000 / (constant_55555 * 5 * 5 * 5 * 5),
0x1000000 / (constant_55555 * constant_55555),
0x1000000 / (constant_55555 * constant_55555 * 5)};
};
template <typename U>
constexpr float binary_format_lookup_tables<float, U>::powers_of_ten[];
template <typename U>
constexpr uint64_t binary_format_lookup_tables<float, U>::max_mantissa[];
template <> inline constexpr int binary_format<double>::min_exponent_fast_path() {
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return 0;
#else
return -22;
#endif
}
template <> inline constexpr int binary_format<float>::min_exponent_fast_path() {
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return 0;
#else
return -10;
#endif
}
template <> inline constexpr int binary_format<double>::mantissa_explicit_bits() {
return 52;
}
template <> inline constexpr int binary_format<float>::mantissa_explicit_bits() {
return 23;
}
template <> inline constexpr int binary_format<double>::max_exponent_round_to_even() {
return 23;
}
template <> inline constexpr int binary_format<float>::max_exponent_round_to_even() {
return 10;
}
template <> inline constexpr int binary_format<double>::min_exponent_round_to_even() {
return -4;
}
template <> inline constexpr int binary_format<float>::min_exponent_round_to_even() {
return -17;
}
template <> inline constexpr int binary_format<double>::minimum_exponent() {
return -1023;
}
template <> inline constexpr int binary_format<float>::minimum_exponent() {
return -127;
}
template <> inline constexpr int binary_format<double>::infinite_power() {
return 0x7FF;
}
template <> inline constexpr int binary_format<float>::infinite_power() {
return 0xFF;
}
template <> inline constexpr int binary_format<double>::sign_index() { return 63; }
template <> inline constexpr int binary_format<float>::sign_index() { return 31; }
template <> inline constexpr int binary_format<double>::max_exponent_fast_path() {
return 22;
}
template <> inline constexpr int binary_format<float>::max_exponent_fast_path() {
return 10;
}
template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits();
}
template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path(int64_t power) {
// caller is responsible to ensure that
// power >= 0 && power <= 22
//
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)max_mantissa[0], max_mantissa[power];
}
template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits();
}
template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path(int64_t power) {
// caller is responsible to ensure that
// power >= 0 && power <= 10
//
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)max_mantissa[0], max_mantissa[power];
}
template <>
inline constexpr double binary_format<double>::exact_power_of_ten(int64_t power) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)powers_of_ten[0], powers_of_ten[power];
}
template <>
inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)powers_of_ten[0], powers_of_ten[power];
}
template <>
inline constexpr int binary_format<double>::largest_power_of_ten() {
return 308;
}
template <>
inline constexpr int binary_format<float>::largest_power_of_ten() {
return 38;
}
template <>
inline constexpr int binary_format<double>::smallest_power_of_ten() {
return -342;
}
template <>
inline constexpr int binary_format<float>::smallest_power_of_ten() {
return -65;
}
template <> inline constexpr size_t binary_format<double>::max_digits() {
return 769;
}
template <> inline constexpr size_t binary_format<float>::max_digits() {
return 114;
}
template <> inline constexpr binary_format<float>::equiv_uint
binary_format<float>::exponent_mask() {
return 0x7F800000;
}
template <> inline constexpr binary_format<double>::equiv_uint
binary_format<double>::exponent_mask() {
return 0x7FF0000000000000;
}
template <> inline constexpr binary_format<float>::equiv_uint
binary_format<float>::mantissa_mask() {
return 0x007FFFFF;
}
template <> inline constexpr binary_format<double>::equiv_uint
binary_format<double>::mantissa_mask() {
return 0x000FFFFFFFFFFFFF;
}
template <> inline constexpr binary_format<float>::equiv_uint
binary_format<float>::hidden_bit_mask() {
return 0x00800000;
}
template <> inline constexpr binary_format<double>::equiv_uint
binary_format<double>::hidden_bit_mask() {
return 0x0010000000000000;
}
template<typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void to_float(bool negative, adjusted_mantissa am, T &value) {
using fastfloat_uint = typename binary_format<T>::equiv_uint;
fastfloat_uint word = (fastfloat_uint)am.mantissa;
word |= fastfloat_uint(am.power2) << binary_format<T>::mantissa_explicit_bits();
word |= fastfloat_uint(negative) << binary_format<T>::sign_index();
#if FASTFLOAT_HAS_BIT_CAST
value = std::bit_cast<T>(word);
#else
::memcpy(&value, &word, sizeof(T));
#endif
}
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
template <typename = void>
struct space_lut {
static constexpr bool value[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
};
template <typename T>
constexpr bool space_lut<T>::value[];
inline constexpr bool is_space(uint8_t c) { return space_lut<>::value[c]; }
#endif
template<typename UC>
static constexpr uint64_t int_cmp_zeros()
{
static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4), "Unsupported character size");
return (sizeof(UC) == 1) ? 0x3030303030303030 : (sizeof(UC) == 2) ? (uint64_t(UC('0')) << 48 | uint64_t(UC('0')) << 32 | uint64_t(UC('0')) << 16 | UC('0')) : (uint64_t(UC('0')) << 32 | UC('0'));
}
template<typename UC>
static constexpr int int_cmp_len()
{
return sizeof(uint64_t) / sizeof(UC);
}
template<typename UC>
static constexpr UC const * str_const_nan()
{
return nullptr;
}
template<>
constexpr char const * str_const_nan<char>()
{
return "nan";
}
template<>
constexpr wchar_t const * str_const_nan<wchar_t>()
{
return L"nan";
}
template<>
constexpr char16_t const * str_const_nan<char16_t>()
{
return u"nan";
}
template<>
constexpr char32_t const * str_const_nan<char32_t>()
{
return U"nan";
}
template<typename UC>
static constexpr UC const * str_const_inf()
{
return nullptr;
}
template<>
constexpr char const * str_const_inf<char>()
{
return "infinity";
}
template<>
constexpr wchar_t const * str_const_inf<wchar_t>()
{
return L"infinity";
}
template<>
constexpr char16_t const * str_const_inf<char16_t>()
{
return u"infinity";
}
template<>
constexpr char32_t const * str_const_inf<char32_t>()
{
return U"infinity";
}
template <typename = void>
struct int_luts {
static constexpr uint8_t chdigit[] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255,
255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
};
static constexpr size_t maxdigits_u64[] = {
64, 41, 32, 28, 25, 23, 22, 21,
20, 19, 18, 18, 17, 17, 16, 16,
16, 16, 15, 15, 15, 15, 14, 14,
14, 14, 14, 14, 14, 13, 13, 13,
13, 13, 13
};
static constexpr uint64_t min_safe_u64[] = {
9223372036854775808ull, 12157665459056928801ull, 4611686018427387904, 7450580596923828125, 4738381338321616896,
3909821048582988049, 9223372036854775808ull, 12157665459056928801ull, 10000000000000000000ull, 5559917313492231481,
2218611106740436992, 8650415919381337933, 2177953337809371136, 6568408355712890625, 1152921504606846976,
2862423051509815793, 6746640616477458432, 15181127029874798299ull, 1638400000000000000, 3243919932521508681,
6221821273427820544, 11592836324538749809ull, 876488338465357824, 1490116119384765625, 2481152873203736576,
4052555153018976267, 6502111422497947648, 10260628712958602189ull, 15943230000000000000ull, 787662783788549761,
1152921504606846976, 1667889514952984961, 2386420683693101056, 3379220508056640625, 4738381338321616896
};
};
template <typename T>
constexpr uint8_t int_luts<T>::chdigit[];
template <typename T>
constexpr size_t int_luts<T>::maxdigits_u64[];
template <typename T>
constexpr uint64_t int_luts<T>::min_safe_u64[];
template <typename UC>
fastfloat_really_inline
constexpr uint8_t ch_to_digit(UC c) { return int_luts<>::chdigit[static_cast<unsigned char>(c)]; }
fastfloat_really_inline
constexpr size_t max_digits_u64(int base) { return int_luts<>::maxdigits_u64[base - 2]; }
// If a u64 is exactly max_digits_u64() in length, this is
// the value below which it has definitely overflowed.
fastfloat_really_inline
constexpr uint64_t min_safe_u64(int base) { return int_luts<>::min_safe_u64[base - 2]; }
} // namespace fast_float
#endif

View File

@ -0,0 +1,301 @@
#ifndef FASTFLOAT_PARSE_NUMBER_H
#define FASTFLOAT_PARSE_NUMBER_H
#include "ascii_number.h"
#include "decimal_to_binary.h"
#include "digit_comparison.h"
#include "float_common.h"
#include <cmath>
#include <cstring>
#include <limits>
#include <system_error>
namespace fast_float {
namespace detail {
/**
* Special case +inf, -inf, nan, infinity, -infinity.
* The case comparisons could be made much faster given that we know that the
* strings a null-free and fixed.
**/
template <typename T, typename UC>
from_chars_result_t<UC> FASTFLOAT_CONSTEXPR14
parse_infnan(UC const * first, UC const * last, T &value) noexcept {
from_chars_result_t<UC> answer{};
answer.ptr = first;
answer.ec = std::errc(); // be optimistic
bool minusSign = false;
if (*first == UC('-')) { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here
minusSign = true;
++first;
}
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
if (*first == UC('+')) {
++first;
}
#endif
if (last - first >= 3) {
if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
answer.ptr = (first += 3);
value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN();
// Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan).
if(first != last && *first == UC('(')) {
for(UC const * ptr = first + 1; ptr != last; ++ptr) {
if (*ptr == UC(')')) {
answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
break;
}
else if(!((UC('a') <= *ptr && *ptr <= UC('z')) || (UC('A') <= *ptr && *ptr <= UC('Z')) || (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_')))
break; // forbidden char, not nan(n-char-seq-opt)
}
}
return answer;
}
if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) {
if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) {
answer.ptr = first + 8;
} else {
answer.ptr = first + 3;
}
value = minusSign ? -std::numeric_limits<T>::infinity() : std::numeric_limits<T>::infinity();
return answer;
}
}
answer.ec = std::errc::invalid_argument;
return answer;
}
/**
* Returns true if the floating-pointing rounding mode is to 'nearest'.
* It is the default on most system. This function is meant to be inexpensive.
* Credit : @mwalcott3
*/
fastfloat_really_inline bool rounds_to_nearest() noexcept {
// https://lemire.me/blog/2020/06/26/gcc-not-nearest/
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return false;
#endif
// See
// A fast function to check your floating-point rounding mode
// https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/
//
// This function is meant to be equivalent to :
// prior: #include <cfenv>
// return fegetround() == FE_TONEAREST;
// However, it is expected to be much faster than the fegetround()
// function call.
//
// The volatile keywoard prevents the compiler from computing the function
// at compile-time.
// There might be other ways to prevent compile-time optimizations (e.g., asm).
// The value does not need to be std::numeric_limits<float>::min(), any small
// value so that 1 + x should round to 1 would do (after accounting for excess
// precision, as in 387 instructions).
static volatile float fmin = std::numeric_limits<float>::min();
float fmini = fmin; // we copy it so that it gets loaded at most once.
//
// Explanation:
// Only when fegetround() == FE_TONEAREST do we have that
// fmin + 1.0f == 1.0f - fmin.
//
// FE_UPWARD:
// fmin + 1.0f > 1
// 1.0f - fmin == 1
//
// FE_DOWNWARD or FE_TOWARDZERO:
// fmin + 1.0f == 1
// 1.0f - fmin < 1
//
// Note: This may fail to be accurate if fast-math has been
// enabled, as rounding conventions may not apply.
#ifdef FASTFLOAT_VISUAL_STUDIO
# pragma warning(push)
// todo: is there a VS warning?
// see https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013
#elif defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wfloat-equal"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
return (fmini + 1.0f == 1.0f - fmini);
#ifdef FASTFLOAT_VISUAL_STUDIO
# pragma warning(pop)
#elif defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
}
} // namespace detail
template <typename T>
struct from_chars_caller
{
template <typename UC>
FASTFLOAT_CONSTEXPR20
static from_chars_result_t<UC> call(UC const * first, UC const * last,
T &value, parse_options_t<UC> options) noexcept {
return from_chars_advanced(first, last, value, options);
}
};
#if __STDCPP_FLOAT32_T__ == 1
template <>
struct from_chars_caller<std::float32_t>
{
template <typename UC>
FASTFLOAT_CONSTEXPR20
static from_chars_result_t<UC> call(UC const * first, UC const * last,
std::float32_t &value, parse_options_t<UC> options) noexcept{
// if std::float32_t is defined, and we are in C++23 mode; macro set for float32;
// set value to float due to equivalence between float and float32_t
float val;
auto ret = from_chars_advanced(first, last, val, options);
value = val;
return ret;
}
};
#endif
#if __STDCPP_FLOAT64_T__ == 1
template <>
struct from_chars_caller<std::float64_t>
{
template <typename UC>
FASTFLOAT_CONSTEXPR20
static from_chars_result_t<UC> call(UC const * first, UC const * last,
std::float64_t &value, parse_options_t<UC> options) noexcept{
// if std::float64_t is defined, and we are in C++23 mode; macro set for float64;
// set value as double due to equivalence between double and float64_t
double val;
auto ret = from_chars_advanced(first, last, val, options);
value = val;
return ret;
}
};
#endif
template<typename T, typename UC, typename>
FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
T &value, chars_format fmt /*= chars_format::general*/) noexcept {
return from_chars_caller<T>::call(first, last, value, parse_options_t<UC>(fmt));
}
template<typename T, typename UC>
FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
T &value, parse_options_t<UC> options) noexcept {
static_assert (is_supported_float_type<T>(), "only some floating-point types are supported");
static_assert (is_supported_char_type<UC>(), "only char, wchar_t, char16_t and char32_t are supported");
from_chars_result_t<UC> answer;
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
while ((first != last) && fast_float::is_space(uint8_t(*first))) {
first++;
}
#endif
if (first == last) {
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
return answer;
}
parsed_number_string_t<UC> pns = parse_number_string<UC>(first, last, options);
if (!pns.valid) {
if (options.format & chars_format::no_infnan) {
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
return answer;
} else {
return detail::parse_infnan(first, last, value);
}
}
answer.ec = std::errc(); // be optimistic
answer.ptr = pns.lastmatch;
// The implementation of the Clinger's fast path is convoluted because
// we want round-to-nearest in all cases, irrespective of the rounding mode
// selected on the thread.
// We proceed optimistically, assuming that detail::rounds_to_nearest() returns
// true.
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && !pns.too_many_digits) {
// Unfortunately, the conventional Clinger's fast path is only possible
// when the system rounds to the nearest float.
//
// We expect the next branch to almost always be selected.
// We could check it first (before the previous branch), but
// there might be performance advantages at having the check
// be last.
if(!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
// We have that fegetround() == FE_TONEAREST.
// Next is Clinger's fast path.
if (pns.mantissa <=binary_format<T>::max_mantissa_fast_path()) {
value = T(pns.mantissa);
if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
if (pns.negative) { value = -value; }
return answer;
}
} else {
// We do not have that fegetround() == FE_TONEAREST.
// Next is a modified Clinger's fast path, inspired by Jakub Jelínek's proposal
if (pns.exponent >= 0 && pns.mantissa <=binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
#if defined(__clang__) || defined(FASTFLOAT_32BIT)
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
if(pns.mantissa == 0) {
value = pns.negative ? T(-0.) : T(0.);
return answer;
}
#endif
value = T(pns.mantissa) * binary_format<T>::exact_power_of_ten(pns.exponent);
if (pns.negative) { value = -value; }
return answer;
}
}
}
adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
if(pns.too_many_digits && am.power2 >= 0) {
if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa);
}
}
// If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0),
// then we need to go the long way around again. This is very uncommon.
if(am.power2 < 0) { am = digit_comp<T>(pns, am); }
to_float(pns.negative, am, value);
// Test for over/underflow.
if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || am.power2 == binary_format<T>::infinite_power()) {
answer.ec = std::errc::result_out_of_range;
}
return answer;
}
template <typename T, typename UC, typename>
FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars(UC const* first, UC const* last, T& value, int base) noexcept {
static_assert (is_supported_char_type<UC>(), "only char, wchar_t, char16_t and char32_t are supported");
from_chars_result_t<UC> answer;
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
while ((first != last) && fast_float::is_space(uint8_t(*first))) {
first++;
}
#endif
if (first == last || base < 2 || base > 36) {
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
return answer;
}
return parse_int_string(first, last, value, base);
}
} // namespace fast_float
#endif

View File

@ -0,0 +1,360 @@
#ifndef FASTFLOAT_GENERIC_DECIMAL_TO_BINARY_H
#define FASTFLOAT_GENERIC_DECIMAL_TO_BINARY_H
/**
* This code is meant to handle the case where we have more than 19 digits.
*
* It is based on work by Nigel Tao (at https://github.com/google/wuffs/)
* who credits Ken Thompson for the design (via a reference to the Go source
* code).
*
* Rob Pike suggested that this algorithm be called "Simple Decimal Conversion".
*
* It is probably not very fast but it is a fallback that should almost never
* be used in real life. Though it is not fast, it is "easily" understood and debugged.
**/
#include "ascii_number.h"
#include "decimal_to_binary.h"
#include <cstdint>
namespace fast_float {
namespace detail {
// remove all final zeroes
inline void trim(decimal &h) {
while ((h.num_digits > 0) && (h.digits[h.num_digits - 1] == 0)) {
h.num_digits--;
}
}
inline uint32_t number_of_digits_decimal_left_shift(const decimal &h, uint32_t shift) {
shift &= 63;
constexpr uint16_t number_of_digits_decimal_left_shift_table[65] = {
0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817,
0x181D, 0x2024, 0x202B, 0x2033, 0x203C, 0x2846, 0x2850, 0x285B, 0x3067,
0x3073, 0x3080, 0x388E, 0x389C, 0x38AB, 0x38BB, 0x40CC, 0x40DD, 0x40EF,
0x4902, 0x4915, 0x4929, 0x513E, 0x5153, 0x5169, 0x5180, 0x5998, 0x59B0,
0x59C9, 0x61E3, 0x61FD, 0x6218, 0x6A34, 0x6A50, 0x6A6D, 0x6A8B, 0x72AA,
0x72C9, 0x72E9, 0x7B0A, 0x7B2B, 0x7B4D, 0x8370, 0x8393, 0x83B7, 0x83DC,
0x8C02, 0x8C28, 0x8C4F, 0x9477, 0x949F, 0x94C8, 0x9CF2, 0x051C, 0x051C,
0x051C, 0x051C,
};
uint32_t x_a = number_of_digits_decimal_left_shift_table[shift];
uint32_t x_b = number_of_digits_decimal_left_shift_table[shift + 1];
uint32_t num_new_digits = x_a >> 11;
uint32_t pow5_a = 0x7FF & x_a;
uint32_t pow5_b = 0x7FF & x_b;
constexpr uint8_t
number_of_digits_decimal_left_shift_table_powers_of_5[0x051C] = {
5, 2, 5, 1, 2, 5, 6, 2, 5, 3, 1, 2, 5, 1, 5, 6, 2, 5, 7, 8, 1, 2, 5, 3,
9, 0, 6, 2, 5, 1, 9, 5, 3, 1, 2, 5, 9, 7, 6, 5, 6, 2, 5, 4, 8, 8, 2, 8,
1, 2, 5, 2, 4, 4, 1, 4, 0, 6, 2, 5, 1, 2, 2, 0, 7, 0, 3, 1, 2, 5, 6, 1,
0, 3, 5, 1, 5, 6, 2, 5, 3, 0, 5, 1, 7, 5, 7, 8, 1, 2, 5, 1, 5, 2, 5, 8,
7, 8, 9, 0, 6, 2, 5, 7, 6, 2, 9, 3, 9, 4, 5, 3, 1, 2, 5, 3, 8, 1, 4, 6,
9, 7, 2, 6, 5, 6, 2, 5, 1, 9, 0, 7, 3, 4, 8, 6, 3, 2, 8, 1, 2, 5, 9, 5,
3, 6, 7, 4, 3, 1, 6, 4, 0, 6, 2, 5, 4, 7, 6, 8, 3, 7, 1, 5, 8, 2, 0, 3,
1, 2, 5, 2, 3, 8, 4, 1, 8, 5, 7, 9, 1, 0, 1, 5, 6, 2, 5, 1, 1, 9, 2, 0,
9, 2, 8, 9, 5, 5, 0, 7, 8, 1, 2, 5, 5, 9, 6, 0, 4, 6, 4, 4, 7, 7, 5, 3,
9, 0, 6, 2, 5, 2, 9, 8, 0, 2, 3, 2, 2, 3, 8, 7, 6, 9, 5, 3, 1, 2, 5, 1,
4, 9, 0, 1, 1, 6, 1, 1, 9, 3, 8, 4, 7, 6, 5, 6, 2, 5, 7, 4, 5, 0, 5, 8,
0, 5, 9, 6, 9, 2, 3, 8, 2, 8, 1, 2, 5, 3, 7, 2, 5, 2, 9, 0, 2, 9, 8, 4,
6, 1, 9, 1, 4, 0, 6, 2, 5, 1, 8, 6, 2, 6, 4, 5, 1, 4, 9, 2, 3, 0, 9, 5,
7, 0, 3, 1, 2, 5, 9, 3, 1, 3, 2, 2, 5, 7, 4, 6, 1, 5, 4, 7, 8, 5, 1, 5,
6, 2, 5, 4, 6, 5, 6, 6, 1, 2, 8, 7, 3, 0, 7, 7, 3, 9, 2, 5, 7, 8, 1, 2,
5, 2, 3, 2, 8, 3, 0, 6, 4, 3, 6, 5, 3, 8, 6, 9, 6, 2, 8, 9, 0, 6, 2, 5,
1, 1, 6, 4, 1, 5, 3, 2, 1, 8, 2, 6, 9, 3, 4, 8, 1, 4, 4, 5, 3, 1, 2, 5,
5, 8, 2, 0, 7, 6, 6, 0, 9, 1, 3, 4, 6, 7, 4, 0, 7, 2, 2, 6, 5, 6, 2, 5,
2, 9, 1, 0, 3, 8, 3, 0, 4, 5, 6, 7, 3, 3, 7, 0, 3, 6, 1, 3, 2, 8, 1, 2,
5, 1, 4, 5, 5, 1, 9, 1, 5, 2, 2, 8, 3, 6, 6, 8, 5, 1, 8, 0, 6, 6, 4, 0,
6, 2, 5, 7, 2, 7, 5, 9, 5, 7, 6, 1, 4, 1, 8, 3, 4, 2, 5, 9, 0, 3, 3, 2,
0, 3, 1, 2, 5, 3, 6, 3, 7, 9, 7, 8, 8, 0, 7, 0, 9, 1, 7, 1, 2, 9, 5, 1,
6, 6, 0, 1, 5, 6, 2, 5, 1, 8, 1, 8, 9, 8, 9, 4, 0, 3, 5, 4, 5, 8, 5, 6,
4, 7, 5, 8, 3, 0, 0, 7, 8, 1, 2, 5, 9, 0, 9, 4, 9, 4, 7, 0, 1, 7, 7, 2,
9, 2, 8, 2, 3, 7, 9, 1, 5, 0, 3, 9, 0, 6, 2, 5, 4, 5, 4, 7, 4, 7, 3, 5,
0, 8, 8, 6, 4, 6, 4, 1, 1, 8, 9, 5, 7, 5, 1, 9, 5, 3, 1, 2, 5, 2, 2, 7,
3, 7, 3, 6, 7, 5, 4, 4, 3, 2, 3, 2, 0, 5, 9, 4, 7, 8, 7, 5, 9, 7, 6, 5,
6, 2, 5, 1, 1, 3, 6, 8, 6, 8, 3, 7, 7, 2, 1, 6, 1, 6, 0, 2, 9, 7, 3, 9,
3, 7, 9, 8, 8, 2, 8, 1, 2, 5, 5, 6, 8, 4, 3, 4, 1, 8, 8, 6, 0, 8, 0, 8,
0, 1, 4, 8, 6, 9, 6, 8, 9, 9, 4, 1, 4, 0, 6, 2, 5, 2, 8, 4, 2, 1, 7, 0,
9, 4, 3, 0, 4, 0, 4, 0, 0, 7, 4, 3, 4, 8, 4, 4, 9, 7, 0, 7, 0, 3, 1, 2,
5, 1, 4, 2, 1, 0, 8, 5, 4, 7, 1, 5, 2, 0, 2, 0, 0, 3, 7, 1, 7, 4, 2, 2,
4, 8, 5, 3, 5, 1, 5, 6, 2, 5, 7, 1, 0, 5, 4, 2, 7, 3, 5, 7, 6, 0, 1, 0,
0, 1, 8, 5, 8, 7, 1, 1, 2, 4, 2, 6, 7, 5, 7, 8, 1, 2, 5, 3, 5, 5, 2, 7,
1, 3, 6, 7, 8, 8, 0, 0, 5, 0, 0, 9, 2, 9, 3, 5, 5, 6, 2, 1, 3, 3, 7, 8,
9, 0, 6, 2, 5, 1, 7, 7, 6, 3, 5, 6, 8, 3, 9, 4, 0, 0, 2, 5, 0, 4, 6, 4,
6, 7, 7, 8, 1, 0, 6, 6, 8, 9, 4, 5, 3, 1, 2, 5, 8, 8, 8, 1, 7, 8, 4, 1,
9, 7, 0, 0, 1, 2, 5, 2, 3, 2, 3, 3, 8, 9, 0, 5, 3, 3, 4, 4, 7, 2, 6, 5,
6, 2, 5, 4, 4, 4, 0, 8, 9, 2, 0, 9, 8, 5, 0, 0, 6, 2, 6, 1, 6, 1, 6, 9,
4, 5, 2, 6, 6, 7, 2, 3, 6, 3, 2, 8, 1, 2, 5, 2, 2, 2, 0, 4, 4, 6, 0, 4,
9, 2, 5, 0, 3, 1, 3, 0, 8, 0, 8, 4, 7, 2, 6, 3, 3, 3, 6, 1, 8, 1, 6, 4,
0, 6, 2, 5, 1, 1, 1, 0, 2, 2, 3, 0, 2, 4, 6, 2, 5, 1, 5, 6, 5, 4, 0, 4,
2, 3, 6, 3, 1, 6, 6, 8, 0, 9, 0, 8, 2, 0, 3, 1, 2, 5, 5, 5, 5, 1, 1, 1,
5, 1, 2, 3, 1, 2, 5, 7, 8, 2, 7, 0, 2, 1, 1, 8, 1, 5, 8, 3, 4, 0, 4, 5,
4, 1, 0, 1, 5, 6, 2, 5, 2, 7, 7, 5, 5, 5, 7, 5, 6, 1, 5, 6, 2, 8, 9, 1,
3, 5, 1, 0, 5, 9, 0, 7, 9, 1, 7, 0, 2, 2, 7, 0, 5, 0, 7, 8, 1, 2, 5, 1,
3, 8, 7, 7, 7, 8, 7, 8, 0, 7, 8, 1, 4, 4, 5, 6, 7, 5, 5, 2, 9, 5, 3, 9,
5, 8, 5, 1, 1, 3, 5, 2, 5, 3, 9, 0, 6, 2, 5, 6, 9, 3, 8, 8, 9, 3, 9, 0,
3, 9, 0, 7, 2, 2, 8, 3, 7, 7, 6, 4, 7, 6, 9, 7, 9, 2, 5, 5, 6, 7, 6, 2,
6, 9, 5, 3, 1, 2, 5, 3, 4, 6, 9, 4, 4, 6, 9, 5, 1, 9, 5, 3, 6, 1, 4, 1,
8, 8, 8, 2, 3, 8, 4, 8, 9, 6, 2, 7, 8, 3, 8, 1, 3, 4, 7, 6, 5, 6, 2, 5,
1, 7, 3, 4, 7, 2, 3, 4, 7, 5, 9, 7, 6, 8, 0, 7, 0, 9, 4, 4, 1, 1, 9, 2,
4, 4, 8, 1, 3, 9, 1, 9, 0, 6, 7, 3, 8, 2, 8, 1, 2, 5, 8, 6, 7, 3, 6, 1,
7, 3, 7, 9, 8, 8, 4, 0, 3, 5, 4, 7, 2, 0, 5, 9, 6, 2, 2, 4, 0, 6, 9, 5,
9, 5, 3, 3, 6, 9, 1, 4, 0, 6, 2, 5,
};
const uint8_t *pow5 =
&number_of_digits_decimal_left_shift_table_powers_of_5[pow5_a];
uint32_t i = 0;
uint32_t n = pow5_b - pow5_a;
for (; i < n; i++) {
if (i >= h.num_digits) {
return num_new_digits - 1;
} else if (h.digits[i] == pow5[i]) {
continue;
} else if (h.digits[i] < pow5[i]) {
return num_new_digits - 1;
} else {
return num_new_digits;
}
}
return num_new_digits;
}
inline uint64_t round(decimal &h) {
if ((h.num_digits == 0) || (h.decimal_point < 0)) {
return 0;
} else if (h.decimal_point > 18) {
return UINT64_MAX;
}
// at this point, we know that h.decimal_point >= 0
uint32_t dp = uint32_t(h.decimal_point);
uint64_t n = 0;
for (uint32_t i = 0; i < dp; i++) {
n = (10 * n) + ((i < h.num_digits) ? h.digits[i] : 0);
}
bool round_up = false;
if (dp < h.num_digits) {
round_up = h.digits[dp] >= 5; // normally, we round up
// but we may need to round to even!
if ((h.digits[dp] == 5) && (dp + 1 == h.num_digits)) {
round_up = h.truncated || ((dp > 0) && (1 & h.digits[dp - 1]));
}
}
if (round_up) {
n++;
}
return n;
}
// computes h * 2^-shift
inline void decimal_left_shift(decimal &h, uint32_t shift) {
if (h.num_digits == 0) {
return;
}
uint32_t num_new_digits = number_of_digits_decimal_left_shift(h, shift);
int32_t read_index = int32_t(h.num_digits - 1);
uint32_t write_index = h.num_digits - 1 + num_new_digits;
uint64_t n = 0;
while (read_index >= 0) {
n += uint64_t(h.digits[read_index]) << shift;
uint64_t quotient = n / 10;
uint64_t remainder = n - (10 * quotient);
if (write_index < max_digits) {
h.digits[write_index] = uint8_t(remainder);
} else if (remainder > 0) {
h.truncated = true;
}
n = quotient;
write_index--;
read_index--;
}
while (n > 0) {
uint64_t quotient = n / 10;
uint64_t remainder = n - (10 * quotient);
if (write_index < max_digits) {
h.digits[write_index] = uint8_t(remainder);
} else if (remainder > 0) {
h.truncated = true;
}
n = quotient;
write_index--;
}
h.num_digits += num_new_digits;
if (h.num_digits > max_digits) {
h.num_digits = max_digits;
}
h.decimal_point += int32_t(num_new_digits);
trim(h);
}
// computes h * 2^shift
inline void decimal_right_shift(decimal &h, uint32_t shift) {
uint32_t read_index = 0;
uint32_t write_index = 0;
uint64_t n = 0;
while ((n >> shift) == 0) {
if (read_index < h.num_digits) {
n = (10 * n) + h.digits[read_index++];
} else if (n == 0) {
return;
} else {
while ((n >> shift) == 0) {
n = 10 * n;
read_index++;
}
break;
}
}
h.decimal_point -= int32_t(read_index - 1);
if (h.decimal_point < -decimal_point_range) { // it is zero
h.num_digits = 0;
h.decimal_point = 0;
h.negative = false;
h.truncated = false;
return;
}
uint64_t mask = (uint64_t(1) << shift) - 1;
while (read_index < h.num_digits) {
uint8_t new_digit = uint8_t(n >> shift);
n = (10 * (n & mask)) + h.digits[read_index++];
h.digits[write_index++] = new_digit;
}
while (n > 0) {
uint8_t new_digit = uint8_t(n >> shift);
n = 10 * (n & mask);
if (write_index < max_digits) {
h.digits[write_index++] = new_digit;
} else if (new_digit > 0) {
h.truncated = true;
}
}
h.num_digits = write_index;
trim(h);
}
} // namespace detail
template <typename binary>
adjusted_mantissa compute_float(decimal &d) {
adjusted_mantissa answer;
if (d.num_digits == 0) {
// should be zero
answer.power2 = 0;
answer.mantissa = 0;
return answer;
}
// At this point, going further, we can assume that d.num_digits > 0.
//
// We want to guard against excessive decimal point values because
// they can result in long running times. Indeed, we do
// shifts by at most 60 bits. We have that log(10**400)/log(2**60) ~= 22
// which is fine, but log(10**299995)/log(2**60) ~= 16609 which is not
// fine (runs for a long time).
//
if(d.decimal_point < -324) {
// We have something smaller than 1e-324 which is always zero
// in binary64 and binary32.
// It should be zero.
answer.power2 = 0;
answer.mantissa = 0;
return answer;
} else if(d.decimal_point >= 310) {
// We have something at least as large as 0.1e310 which is
// always infinite.
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
return answer;
}
constexpr uint32_t max_shift = 60;
constexpr uint32_t num_powers = 19;
constexpr uint8_t decimal_powers[19] = {
0, 3, 6, 9, 13, 16, 19, 23, 26, 29, //
33, 36, 39, 43, 46, 49, 53, 56, 59, //
};
int32_t exp2 = 0;
while (d.decimal_point > 0) {
uint32_t n = uint32_t(d.decimal_point);
uint32_t shift = (n < num_powers) ? decimal_powers[n] : max_shift;
detail::decimal_right_shift(d, shift);
if (d.decimal_point < -decimal_point_range) {
// should be zero
answer.power2 = 0;
answer.mantissa = 0;
return answer;
}
exp2 += int32_t(shift);
}
// We shift left toward [1/2 ... 1].
while (d.decimal_point <= 0) {
uint32_t shift;
if (d.decimal_point == 0) {
if (d.digits[0] >= 5) {
break;
}
shift = (d.digits[0] < 2) ? 2 : 1;
} else {
uint32_t n = uint32_t(-d.decimal_point);
shift = (n < num_powers) ? decimal_powers[n] : max_shift;
}
detail::decimal_left_shift(d, shift);
if (d.decimal_point > decimal_point_range) {
// we want to get infinity:
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
return answer;
}
exp2 -= int32_t(shift);
}
// We are now in the range [1/2 ... 1] but the binary format uses [1 ... 2].
exp2--;
constexpr int32_t minimum_exponent = binary::minimum_exponent();
while ((minimum_exponent + 1) > exp2) {
uint32_t n = uint32_t((minimum_exponent + 1) - exp2);
if (n > max_shift) {
n = max_shift;
}
detail::decimal_right_shift(d, n);
exp2 += int32_t(n);
}
if ((exp2 - minimum_exponent) >= binary::infinite_power()) {
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
return answer;
}
const int mantissa_size_in_bits = binary::mantissa_explicit_bits() + 1;
detail::decimal_left_shift(d, mantissa_size_in_bits);
uint64_t mantissa = detail::round(d);
// It is possible that we have an overflow, in which case we need
// to shift back.
if(mantissa >= (uint64_t(1) << mantissa_size_in_bits)) {
detail::decimal_right_shift(d, 1);
exp2 += 1;
mantissa = detail::round(d);
if ((exp2 - minimum_exponent) >= binary::infinite_power()) {
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
return answer;
}
}
answer.power2 = exp2 - binary::minimum_exponent();
if(mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) { answer.power2--; }
answer.mantissa = mantissa & ((uint64_t(1) << binary::mantissa_explicit_bits()) - 1);
return answer;
}
template <typename binary>
adjusted_mantissa parse_long_mantissa(const char *first, const char* last, parse_options options) {
decimal d = parse_decimal(first, last, options);
return compute_float<binary>(d);
}
} // namespace fast_float
#endif

76
3rdparty/rapidyaml/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,76 @@
add_library(pcsx2-rapidyaml
include/c4/base64.hpp
include/c4/blob.hpp
include/c4/charconv.hpp
include/c4/compiler.hpp
include/c4/config.hpp
include/c4/cpu.hpp
include/c4/dump.hpp
include/c4/error.hpp
include/c4/export.hpp
include/c4/format.hpp
include/c4/language.hpp
include/c4/memory_util.hpp
include/c4/platform.hpp
include/c4/preprocessor.hpp
include/c4/std/std.hpp
include/c4/std/std_fwd.hpp
include/c4/std/string.hpp
include/c4/std/string_fwd.hpp
include/c4/std/string_view.hpp
include/c4/std/tuple.hpp
include/c4/std/vector.hpp
include/c4/std/vector_fwd.hpp
include/c4/substr.hpp
include/c4/substr_fwd.hpp
include/c4/szconv.hpp
include/c4/types.hpp
include/c4/utf.hpp
include/c4/windows.hpp
include/c4/windows_pop.hpp
include/c4/windows_push.hpp
include/c4/yml/common.hpp
include/c4/yml/detail/parser_dbg.hpp
include/c4/yml/detail/stack.hpp
include/c4/yml/emit.def.hpp
include/c4/yml/emit.hpp
include/c4/yml/export.hpp
include/c4/yml/node.hpp
include/c4/yml/parse.hpp
include/c4/yml/preprocess.hpp
include/c4/yml/std/map.hpp
include/c4/yml/std/std.hpp
include/c4/yml/std/string.hpp
include/c4/yml/std/vector.hpp
include/c4/yml/tree.hpp
include/c4/yml/writer.hpp
include/c4/yml/yml.hpp
include/ryml.hpp
include/ryml_std.hpp
src/c4/base64.cpp
src/c4/error.cpp
src/c4/format.cpp
src/c4/language.cpp
src/c4/memory_util.cpp
src/c4/utf.cpp
src/c4/yml/common.cpp
src/c4/yml/node.cpp
src/c4/yml/parse.cpp
src/c4/yml/preprocess.cpp
src/c4/yml/tree.cpp
)
target_include_directories(pcsx2-rapidyaml PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}/src"
"${CMAKE_CURRENT_SOURCE_DIR}/../fast_float/include"
)
target_include_directories(pcsx2-rapidyaml INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include"
)
target_compile_definitions(pcsx2-rapidyaml PUBLIC
"C4_NO_DEBUG_BREAK"
)
add_library(rapidyaml::rapidyaml ALIAS pcsx2-rapidyaml)

141
3rdparty/rapidyaml/include/c4/base64.hpp vendored Normal file
View File

@ -0,0 +1,141 @@
#ifndef _C4_BASE64_HPP_
#define _C4_BASE64_HPP_
/** @file base64.hpp encoding/decoding for base64.
* @see https://en.wikipedia.org/wiki/Base64
* @see https://www.base64encode.org/
* */
#include "c4/charconv.hpp"
#include "c4/blob.hpp"
namespace c4 {
/** @defgroup doc_base64 Base64 encoding/decoding
* @see https://en.wikipedia.org/wiki/Base64
* @see https://www.base64encode.org/
* @{ */
/** check that the given buffer is a valid base64 encoding
* @see https://en.wikipedia.org/wiki/Base64 */
C4CORE_EXPORT bool base64_valid(csubstr encoded);
/** base64-encode binary data.
* @param encoded [out] output buffer for encoded data
* @param data [in] the input buffer with the binary data
*
* @return the number of bytes needed to return the output (ie the
* required size for @p encoded). No writes occur beyond the end of
* the output buffer, so it is safe to do a speculative call where the
* encoded buffer is empty, or maybe too small. The caller should
* ensure that the returned size is smaller than the size of the
* encoded buffer.
*
* @note the result depends on endianness. If transfer between
* little/big endian systems is desired, the caller should normalize
* @p data before encoding.
*
* @see https://en.wikipedia.org/wiki/Base64 */
C4CORE_EXPORT size_t base64_encode(substr encoded, cblob data);
/** decode the base64 encoding in the given buffer
* @param encoded [in] the encoded base64
* @param data [out] the output buffer
*
* @return the number of bytes needed to return the output (ie the
* required size for @p data). No writes occur beyond the end of the
* output buffer, so it is safe to do a speculative call where the
* data buffer is empty, or maybe too small. The caller should ensure
* that the returned size is smaller than the size of the data buffer.
*
* @note the result depends on endianness. If transfer between
* little/big endian systems is desired, the caller should normalize
* @p data after decoding.
*
* @see https://en.wikipedia.org/wiki/Base64 */
C4CORE_EXPORT size_t base64_decode(csubstr encoded, blob data);
/** @} */ // base64
namespace fmt {
/** @addtogroup doc_format_specifiers
* @{ */
/** @defgroup doc_base64_fmt Base64
* @{ */
template<typename CharOrConstChar>
struct base64_wrapper_
{
blob_<CharOrConstChar> data;
base64_wrapper_() : data() {}
base64_wrapper_(blob_<CharOrConstChar> blob) : data(blob) {}
};
/** a tag type to mark a payload as base64-encoded */
using const_base64_wrapper = base64_wrapper_<cbyte>;
/** a tag type to mark a payload to be encoded as base64 */
using base64_wrapper = base64_wrapper_<byte>;
/** mark a variable to be written in base64 format */
template<class ...Args>
C4_ALWAYS_INLINE const_base64_wrapper cbase64(Args const& C4_RESTRICT ...args)
{
return const_base64_wrapper(cblob(args...));
}
/** mark a csubstr to be written in base64 format */
C4_ALWAYS_INLINE const_base64_wrapper cbase64(csubstr s)
{
return const_base64_wrapper(cblob(s.str, s.len));
}
/** mark a variable to be written in base64 format */
template<class ...Args>
C4_ALWAYS_INLINE const_base64_wrapper base64(Args const& C4_RESTRICT ...args)
{
return const_base64_wrapper(cblob(args...));
}
/** mark a csubstr to be written in base64 format */
C4_ALWAYS_INLINE const_base64_wrapper base64(csubstr s)
{
return const_base64_wrapper(cblob(s.str, s.len));
}
/** mark a variable to be read in base64 format */
template<class ...Args>
C4_ALWAYS_INLINE base64_wrapper base64(Args &... args)
{
return base64_wrapper(blob(args...));
}
/** mark a variable to be read in base64 format */
C4_ALWAYS_INLINE base64_wrapper base64(substr s)
{
return base64_wrapper(blob(s.str, s.len));
}
/** @} */ // base64_fmt
/** @} */ // format_specifiers
} // namespace fmt
/** write a variable in base64 format
* @ingroup doc_to_chars */
inline size_t to_chars(substr buf, fmt::const_base64_wrapper b)
{
return base64_encode(buf, b.data);
}
/** read a variable in base64 format
* @ingroup doc_from_chars */
inline size_t from_chars(csubstr buf, fmt::base64_wrapper *b)
{
return base64_decode(buf, b->data);
}
} // namespace c4
#endif /* _C4_BASE64_HPP_ */

67
3rdparty/rapidyaml/include/c4/blob.hpp vendored Normal file
View File

@ -0,0 +1,67 @@
#ifndef _C4_BLOB_HPP_
#define _C4_BLOB_HPP_
#include "c4/types.hpp"
#include "c4/error.hpp"
/** @file blob.hpp Mutable and immutable binary data blobs.
*/
namespace c4 {
template<class T>
struct blob_;
namespace detail {
template<class T> struct is_blob_type : std::integral_constant<bool, false> {};
template<class T> struct is_blob_type<blob_<T>> : std::integral_constant<bool, true> {};
template<class T> struct is_blob_value_type : std::integral_constant<bool, (std::is_fundamental<T>::value || std::is_trivially_copyable<T>::value)> {};
} // namespace
template<class T>
struct blob_
{
static_assert(std::is_same<T, byte>::value || std::is_same<T, cbyte>::value, "must be either byte or cbyte");
static_assert(sizeof(T) == 1u, "must be either byte or cbyte");
public:
T * buf;
size_t len;
public:
C4_ALWAYS_INLINE blob_() noexcept = default;
C4_ALWAYS_INLINE blob_(blob_ const& that) noexcept = default;
C4_ALWAYS_INLINE blob_(blob_ && that) noexcept = default;
C4_ALWAYS_INLINE blob_& operator=(blob_ && that) noexcept = default;
C4_ALWAYS_INLINE blob_& operator=(blob_ const& that) noexcept = default;
template<class U, class=typename std::enable_if<std::is_const<T>::value && std::is_same<typename std::add_const<U>::type, T>::value, U>::type> C4_ALWAYS_INLINE blob_(blob_<U> const& that) noexcept : buf(that.buf), len(that.len) {}
template<class U, class=typename std::enable_if<std::is_const<T>::value && std::is_same<typename std::add_const<U>::type, T>::value, U>::type> C4_ALWAYS_INLINE blob_(blob_<U> && that) noexcept : buf(that.buf), len(that.len) {}
template<class U, class=typename std::enable_if<std::is_const<T>::value && std::is_same<typename std::add_const<U>::type, T>::value, U>::type> C4_ALWAYS_INLINE blob_& operator=(blob_<U> && that) noexcept { buf = that.buf; len = that.len; }
template<class U, class=typename std::enable_if<std::is_const<T>::value && std::is_same<typename std::add_const<U>::type, T>::value, U>::type> C4_ALWAYS_INLINE blob_& operator=(blob_<U> const& that) noexcept { buf = that.buf; len = that.len; }
C4_ALWAYS_INLINE blob_(void *ptr, size_t n) noexcept : buf(reinterpret_cast<T*>(ptr)), len(n) {}
C4_ALWAYS_INLINE blob_(void const *ptr, size_t n) noexcept : buf(reinterpret_cast<T*>(ptr)), len(n) {}
#define _C4_REQUIRE_BLOBTYPE(ty) class=typename std::enable_if<((!detail::is_blob_type<ty>::value) && (detail::is_blob_value_type<ty>::value)), T>::type
template<class U, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_(U &var) noexcept : buf(reinterpret_cast<T*>(&var)), len(sizeof(U)) {}
template<class U, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_(U *ptr, size_t n) noexcept : buf(reinterpret_cast<T*>(ptr)), len(sizeof(U) * n) { C4_ASSERT(is_aligned(ptr)); }
template<class U, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_& operator= (U &var) noexcept { buf = reinterpret_cast<T*>(&var); len = sizeof(U); return *this; }
template<class U, size_t N, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_(U (&arr)[N]) noexcept : buf(reinterpret_cast<T*>(arr)), len(sizeof(U) * N) {}
template<class U, size_t N, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_& operator= (U (&arr)[N]) noexcept { buf = reinterpret_cast<T*>(arr); len = sizeof(U) * N; return *this; }
#undef _C4_REQUIRE_BLOBTYPE
};
/** an immutable binary blob */
using cblob = blob_<cbyte>;
/** a mutable binary blob */
using blob = blob_< byte>;
C4_MUST_BE_TRIVIAL_COPY(blob);
C4_MUST_BE_TRIVIAL_COPY(cblob);
} // namespace c4
#endif // _C4_BLOB_HPP_

View File

@ -0,0 +1,168 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Very good intro:
@see https://code.msdn.microsoft.com/windowsdesktop/Writing-type-visualizers-2eae77a2
See also:
@see http://blogs.msdn.com/b/vcblog/archive/2013/06/28/using-visual-studio-2013-to-write-maintainable-native-visualizations-natvis.aspx?PageIndex=2
@see http://blogs.msdn.com/b/vcblog/archive/2015/09/28/debug-visualizers-in-visual-c-2015.aspx
@see http://stackoverflow.com/questions/36883414/limit-display-of-char-in-natvis-file-to-specific-length
-->
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="c4::basic_substring&lt;*&gt;">
<DisplayString>{str,[len]} (sz={len})</DisplayString>
<StringView>str,[len]</StringView>
<Expand>
<Item Name="[size]">len</Item>
<ArrayItems>
<Size>len</Size>
<ValuePointer>str</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="c4::span&lt;*&gt;">
<DisplayString>{m_ptr,[m_size]} (sz={m_size})</DisplayString>
<Expand>
<Item Name="[size]">m_size</Item>
<ArrayItems>
<Size>m_size</Size>
<ValuePointer>m_ptr</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="c4::spanrs&lt;*&gt;">
<DisplayString>{m_ptr,[m_size]} (sz={m_size}, cap={m_capacity})</DisplayString>
<Expand>
<Item Name="[size]">m_size</Item>
<Item Name="[capacity]">m_capacity</Item>
<ArrayItems>
<Size>m_size</Size>
<ValuePointer>m_ptr</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<!-- display span<char>/span<const char> as a string too -->
<Type Name="c4::span&lt;char,*&gt;">
<DisplayString>{m_ptr,[m_size]} (sz={m_size})</DisplayString>
<StringView>m_ptr,[m_size]</StringView>
<Expand>
<Item Name="[size]">m_size</Item>
<ArrayItems>
<Size>m_size</Size>
<ValuePointer>m_ptr</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="c4::span&lt;const char,*&gt;">
<DisplayString>{m_ptr,[m_size]} (sz={m_size})</DisplayString>
<StringView>m_ptr,[m_size]</StringView>
<Expand>
<Item Name="[size]">m_size</Item>
<ArrayItems>
<Size>m_size</Size>
<ValuePointer>m_ptr</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<!-- display spanrs<char>/spanrs<const char> as a string too -->
<Type Name="c4::spanrs&lt;char,*&gt;">
<DisplayString>{m_ptr,[m_size]} (sz={m_size}, cap={m_capacity})</DisplayString>
<StringView>m_ptr,[m_size]</StringView>
<Expand>
<Item Name="[size]">m_size</Item>
<Item Name="[capacity]">m_capacity</Item>
<ArrayItems>
<Size>m_size</Size>
<ValuePointer>m_ptr</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="c4::spanrs&lt;const char,*&gt;">
<DisplayString>{m_ptr,[m_size]} (sz={m_size}, cap={m_capacity})</DisplayString>
<StringView>m_ptr,[m_size]</StringView>
<Expand>
<Item Name="[size]">m_size</Item>
<Item Name="[capacity]">m_capacity</Item>
<ArrayItems>
<Size>m_size</Size>
<ValuePointer>m_ptr</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<!-- =========================================================================================== -->
<Type Name="c4::string_impl&lt;*,*,*,*&gt;">
<DisplayString>{(($T3*)this)->m_str,[(($T3*)this)->m_size]} (sz={(($T3*)this)->m_size})</DisplayString>
<StringView>(($T3*)this)->m_str,[(($T3*)this)->m_size]</StringView>
<Expand>
<Synthetic Name="m_str">
<DisplayString>{(($T3*)this)->m_str,[(($T3*)this)->m_size]}</DisplayString>
<StringView>(($T3*)this)->m_str,[(($T3*)this)->m_size]</StringView>
</Synthetic>
<Synthetic Name="m_size">
<DisplayString>{(($T3*)this)->m_size}</DisplayString>
</Synthetic>
</Expand>
</Type>
<Type Name="c4::basic_substring&lt;*,*&gt;">
<DisplayString>{m_str,[m_size]} (sz={m_size})</DisplayString>
<StringView>m_str,[m_size]</StringView>
<Expand>
<Synthetic Name="[size]">
<DisplayString>{m_size}</DisplayString>
</Synthetic>
</Expand>
</Type>
<Type Name="c4::basic_substringrs&lt;*,*&gt;">
<DisplayString>{m_str,[m_size]} (sz={m_size},cap={m_capacity})</DisplayString>
<StringView>m_str,[m_size]</StringView>
<Expand>
<Synthetic Name="[size]">
<DisplayString>{m_size}</DisplayString>
</Synthetic>
<Synthetic Name="[capacity]">
<DisplayString>{m_capacity}</DisplayString>
</Synthetic>
<Synthetic Name="[full]">
<DisplayString>{m_str,[m_capacity]}</DisplayString>
<StringView>m_str,[m_capacity]</StringView>
</Synthetic>
</Expand>
</Type>
<Type Name="c4::basic_string&lt;*,*,*&gt;">
<DisplayString>{m_str,[m_size]} (sz={m_size},cap={m_capacity})</DisplayString>
<StringView>m_str,[m_size]</StringView>
<Expand>
<Synthetic Name="[size]">
<DisplayString>{m_size}</DisplayString>
</Synthetic>
<Synthetic Name="[full]">
<DisplayString>{m_str,[m_capacity]}</DisplayString>
<StringView>m_str,[m_capacity]</StringView>
</Synthetic>
</Expand>
</Type>
<!-- enum symbols -->
<Type Name="c4::EnumSymbols&lt;*&gt;::Sym">
<DisplayString>{value} - {name}</DisplayString>
<Expand>
<Item Name="[value]">value</Item>
<Item Name="[name]">name</Item>
</Expand>
</Type>
<Type Name="c4::EnumSymbols&lt;*&gt;">
<DisplayString>{m_symbols,[m_num]} (sz={m_num})</DisplayString>
<Expand>
<Item Name="[size]">m_num</Item>
<ArrayItems>
<Size>m_num</Size>
<ValuePointer>m_symbols</ValuePointer>
</ArrayItems>
</Expand>
</Type>
</AutoVisualizer>

2670
3rdparty/rapidyaml/include/c4/charconv.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,117 @@
#ifndef _C4_COMPILER_HPP_
#define _C4_COMPILER_HPP_
/** @file compiler.hpp Provides compiler information macros
* @ingroup basic_headers */
#include "c4/platform.hpp"
// Compilers:
// C4_MSVC
// Visual Studio 2022: MSVC++ 17, 1930
// Visual Studio 2019: MSVC++ 16, 1920
// Visual Studio 2017: MSVC++ 15
// Visual Studio 2015: MSVC++ 14
// Visual Studio 2013: MSVC++ 13
// Visual Studio 2013: MSVC++ 12
// Visual Studio 2012: MSVC++ 11
// Visual Studio 2010: MSVC++ 10
// Visual Studio 2008: MSVC++ 09
// Visual Studio 2005: MSVC++ 08
// C4_CLANG
// C4_GCC
// C4_ICC (intel compiler)
/** @see http://sourceforge.net/p/predef/wiki/Compilers/ for a list of compiler identifier macros */
/** @see https://msdn.microsoft.com/en-us/library/b0084kay.aspx for VS2013 predefined macros */
#if defined(_MSC_VER) && !defined(__clang__)
# define C4_MSVC
# define C4_MSVC_VERSION_2022 17
# define C4_MSVC_VERSION_2019 16
# define C4_MSVC_VERSION_2017 15
# define C4_MSVC_VERSION_2015 14
# define C4_MSVC_VERSION_2013 12
# define C4_MSVC_VERSION_2012 11
# if _MSC_VER >= 1930
# define C4_MSVC_VERSION C4_MSVC_VERSION_2022 // visual studio 2022
# define C4_MSVC_2022
# elif _MSC_VER >= 1920
# define C4_MSVC_VERSION C4_MSVC_VERSION_2019 // visual studio 2019
# define C4_MSVC_2019
# elif _MSC_VER >= 1910
# define C4_MSVC_VERSION C4_MSVC_VERSION_2017 // visual studio 2017
# define C4_MSVC_2017
# elif _MSC_VER == 1900
# define C4_MSVC_VERSION C4_MSVC_VERSION_2015 // visual studio 2015
# define C4_MSVC_2015
# elif _MSC_VER == 1800
# error "MSVC version not supported"
# define C4_MSVC_VERSION C4_MSVC_VERSION_2013 // visual studio 2013
# define C4_MSVC_2013
# elif _MSC_VER == 1700
# error "MSVC version not supported"
# define C4_MSVC_VERSION C4_MSVC_VERSION_2012 // visual studio 2012
# define C4_MSVC_2012
# elif _MSC_VER == 1600
# error "MSVC version not supported"
# define C4_MSVC_VERSION 10 // visual studio 2010
# define C4_MSVC_2010
# elif _MSC_VER == 1500
# error "MSVC version not supported"
# define C4_MSVC_VERSION 09 // visual studio 2008
# define C4_MSVC_2008
# elif _MSC_VER == 1400
# error "MSVC version not supported"
# define C4_MSVC_VERSION 08 // visual studio 2005
# define C4_MSVC_2005
# else
# error "MSVC version not supported"
# endif // _MSC_VER
#else
# define C4_MSVC_VERSION 0 // visual studio not present
# define C4_GCC_LIKE
# ifdef __INTEL_COMPILER // check ICC before checking GCC, as ICC defines __GNUC__ too
# define C4_ICC
# define C4_ICC_VERSION __INTEL_COMPILER
# elif defined(__APPLE_CC__)
# define C4_XCODE
# if defined(__clang__)
# define C4_CLANG
# ifndef __apple_build_version__
# define C4_CLANG_VERSION C4_VERSION_ENCODED(__clang_major__, __clang_minor__, __clang_patchlevel__)
# else
# define C4_CLANG_VERSION __apple_build_version__
# endif
# else
# define C4_XCODE_VERSION __APPLE_CC__
# endif
# elif defined(__clang__)
# define C4_CLANG
# ifndef __apple_build_version__
# define C4_CLANG_VERSION C4_VERSION_ENCODED(__clang_major__, __clang_minor__, __clang_patchlevel__)
# else
# define C4_CLANG_VERSION __apple_build_version__
# endif
# elif defined(__GNUC__)
# define C4_GCC
# if defined(__GNUC_PATCHLEVEL__)
# define C4_GCC_VERSION C4_VERSION_ENCODED(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
# else
# define C4_GCC_VERSION C4_VERSION_ENCODED(__GNUC__, __GNUC_MINOR__, 0)
# endif
# if __GNUC__ < 5
# if __GNUC__ == 4 && __GNUC_MINOR__ >= 8
// provided by cmake sub-project
# include "c4/gcc-4.8.hpp"
# else
// we do not support GCC < 4.8:
// * misses std::is_trivially_copyable
// * misses std::align
// * -Wshadow has false positives when a local function parameter has the same name as a method
# error "GCC < 4.8 is not supported"
# endif
# endif
# endif
#endif // defined(C4_WIN) && defined(_MSC_VER)
#endif /* _C4_COMPILER_HPP_ */

View File

@ -0,0 +1,39 @@
#ifndef _C4_CONFIG_HPP_
#define _C4_CONFIG_HPP_
/** @defgroup basic_headers Basic headers
* @brief Headers providing basic macros, platform+cpu+compiler information,
* C++ facilities and basic typedefs. */
/** @file config.hpp Contains configuration defines and includes the basic_headers.
* @ingroup basic_headers */
//#define C4_DEBUG
#define C4_ERROR_SHOWS_FILELINE
//#define C4_ERROR_SHOWS_FUNC
//#define C4_ERROR_THROWS_EXCEPTION
//#define C4_NO_ALLOC_DEFAULTS
//#define C4_REDEFINE_CPPNEW
#ifndef C4_SIZE_TYPE
# define C4_SIZE_TYPE size_t
#endif
#ifndef C4_STR_SIZE_TYPE
# define C4_STR_SIZE_TYPE C4_SIZE_TYPE
#endif
#ifndef C4_TIME_TYPE
# define C4_TIME_TYPE double
#endif
#include "c4/export.hpp"
#include "c4/preprocessor.hpp"
#include "c4/platform.hpp"
#include "c4/cpu.hpp"
#include "c4/compiler.hpp"
#include "c4/language.hpp"
#include "c4/types.hpp"
#endif // _C4_CONFIG_HPP_

159
3rdparty/rapidyaml/include/c4/cpu.hpp vendored Normal file
View File

@ -0,0 +1,159 @@
#ifndef _C4_CPU_HPP_
#define _C4_CPU_HPP_
/** @file cpu.hpp Provides processor information macros
* @ingroup basic_headers */
// see also https://sourceforge.net/p/predef/wiki/Architectures/
// see also https://sourceforge.net/p/predef/wiki/Endianness/
// see also https://github.com/googlesamples/android-ndk/blob/android-mk/hello-jni/jni/hello-jni.c
// see http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/global/qprocessordetection.h
#ifdef __ORDER_LITTLE_ENDIAN__
# define _C4EL __ORDER_LITTLE_ENDIAN__
#else
# define _C4EL 1234
#endif
#ifdef __ORDER_BIG_ENDIAN__
# define _C4EB __ORDER_BIG_ENDIAN__
#else
# define _C4EB 4321
#endif
// mixed byte order (eg, PowerPC or ia64)
#define _C4EM 1111
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64)
# define C4_CPU_X86_64
# define C4_WORDSIZE 8
# define C4_BYTE_ORDER _C4EL
#elif defined(__i386) || defined(__i386__) || defined(_M_IX86)
# define C4_CPU_X86
# define C4_WORDSIZE 4
# define C4_BYTE_ORDER _C4EL
#elif defined(__arm__) || defined(_M_ARM) \
|| defined(__TARGET_ARCH_ARM) || defined(__aarch64__) || defined(_M_ARM64)
# if defined(__aarch64__) || defined(_M_ARM64)
# define C4_CPU_ARM64
# define C4_CPU_ARMV8
# define C4_WORDSIZE 8
# else
# define C4_CPU_ARM
# define C4_WORDSIZE 4
# if defined(__ARM_ARCH_8__) || defined(__ARM_ARCH_8A__) \
|| (defined(__ARCH_ARM) && __ARCH_ARM >= 8) \
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 8)
# define C4_CPU_ARMV8
# elif defined(__ARM_ARCH_7__) || defined(_ARM_ARCH_7) \
|| defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) \
|| defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) \
|| defined(__ARM_ARCH_7EM__) \
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 7) \
|| (defined(_M_ARM) && _M_ARM >= 7)
# define C4_CPU_ARMV7
# elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \
|| defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_6Z__) \
|| defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6ZK__) \
|| defined(__ARM_ARCH_6M__) || defined(__ARM_ARCH_6KZ__) \
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 6)
# define C4_CPU_ARMV6
# elif defined(__ARM_ARCH_5TEJ__) \
|| defined(__ARM_ARCH_5TE__) \
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 5)
# define C4_CPU_ARMV5
# elif defined(__ARM_ARCH_4T__) \
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 4)
# define C4_CPU_ARMV4
# else
# error "unknown CPU architecture: ARM"
# endif
# endif
# if defined(__ARMEL__) || defined(__LITTLE_ENDIAN__) || defined(__AARCH64EL__) \
|| (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) \
|| defined(_MSC_VER) // winarm64 does not provide any of the above macros,
// but advises little-endianess:
// https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions?view=msvc-170
// So if it is visual studio compiling, we'll assume little endian.
# define C4_BYTE_ORDER _C4EL
# elif defined(__ARMEB__) || defined(__BIG_ENDIAN__) || defined(__AARCH64EB__) \
|| (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
# define C4_BYTE_ORDER _C4EB
# elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_PDP_ENDIAN__)
# define C4_BYTE_ORDER _C4EM
# else
# error "unknown endianness"
# endif
#elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64)
# define C4_CPU_IA64
# define C4_WORDSIZE 8
# define C4_BYTE_ORDER _C4EM
// itanium is bi-endian - check byte order below
#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) \
|| defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \
|| defined(_M_MPPC) || defined(_M_PPC)
# if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__)
# define C4_CPU_PPC64
# define C4_WORDSIZE 8
# else
# define C4_CPU_PPC
# define C4_WORDSIZE 4
# endif
# define C4_BYTE_ORDER _C4EM
// ppc is bi-endian - check byte order below
#elif defined(__s390x__) || defined(__zarch__) || defined(__SYSC_ZARCH_)
# define C4_CPU_S390_X
# define C4_WORDSIZE 8
# define C4_BYTE_ORDER _C4EB
#elif defined(__xtensa__) || defined(__XTENSA__)
# define C4_CPU_XTENSA
# define C4_WORDSIZE 4
// not sure about this...
# if defined(__XTENSA_EL__) || defined(__xtensa_el__)
# define C4_BYTE_ORDER _C4EL
# else
# define C4_BYTE_ORDER _C4EB
# endif
#elif defined(__riscv)
# if __riscv_xlen == 64
# define C4_CPU_RISCV64
# define C4_WORDSIZE 8
# else
# define C4_CPU_RISCV32
# define C4_WORDSIZE 4
# endif
# define C4_BYTE_ORDER _C4EL
#elif defined(__EMSCRIPTEN__)
# define C4_BYTE_ORDER _C4EL
# define C4_WORDSIZE 4
#elif defined(__loongarch__)
# if defined(__loongarch64)
# define C4_CPU_LOONGARCH64
# define C4_WORDSIZE 8
# else
# define C4_CPU_LOONGARCH
# define C4_WORDSIZE 4
# endif
# define C4_BYTE_ORDER _C4EL
#elif defined(SWIG)
# error "please define CPU architecture macros when compiling with swig"
#else
# error "unknown CPU architecture"
#endif
#define C4_LITTLE_ENDIAN (C4_BYTE_ORDER == _C4EL)
#define C4_BIG_ENDIAN (C4_BYTE_ORDER == _C4EB)
#define C4_MIXED_ENDIAN (C4_BYTE_ORDER == _C4EM)
#endif /* _C4_CPU_HPP_ */

587
3rdparty/rapidyaml/include/c4/dump.hpp vendored Normal file
View File

@ -0,0 +1,587 @@
#ifndef C4_DUMP_HPP_
#define C4_DUMP_HPP_
#include <c4/substr.hpp>
namespace c4 {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** type of the function to dump characters */
using DumperPfn = void (*)(csubstr buf);
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template<DumperPfn dumpfn, class Arg>
inline size_t dump(substr buf, Arg const& a)
{
size_t sz = to_chars(buf, a); // need to serialize to the buffer
if(C4_LIKELY(sz <= buf.len))
dumpfn(buf.first(sz));
return sz;
}
template<class DumperFn, class Arg>
inline size_t dump(DumperFn &&dumpfn, substr buf, Arg const& a)
{
size_t sz = to_chars(buf, a); // need to serialize to the buffer
if(C4_LIKELY(sz <= buf.len))
dumpfn(buf.first(sz));
return sz;
}
template<DumperPfn dumpfn>
inline size_t dump(substr buf, csubstr a)
{
if(buf.len)
dumpfn(a); // dump directly, no need to serialize to the buffer
return 0; // no space was used in the buffer
}
template<class DumperFn>
inline size_t dump(DumperFn &&dumpfn, substr buf, csubstr a)
{
if(buf.len)
dumpfn(a); // dump directly, no need to serialize to the buffer
return 0; // no space was used in the buffer
}
template<DumperPfn dumpfn, size_t N>
inline size_t dump(substr buf, const char (&a)[N])
{
if(buf.len)
dumpfn(csubstr(a)); // dump directly, no need to serialize to the buffer
return 0; // no space was used in the buffer
}
template<class DumperFn, size_t N>
inline size_t dump(DumperFn &&dumpfn, substr buf, const char (&a)[N])
{
if(buf.len)
dumpfn(csubstr(a)); // dump directly, no need to serialize to the buffer
return 0; // no space was used in the buffer
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** */
struct DumpResults
{
enum : size_t { noarg = (size_t)-1 };
size_t bufsize = 0;
size_t lastok = noarg;
bool success_until(size_t expected) const { return lastok == noarg ? false : lastok >= expected; }
bool write_arg(size_t arg) const { return lastok == noarg || arg > lastok; }
size_t argfail() const { return lastok + 1; }
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
// terminates the variadic recursion
template<class DumperFn>
size_t cat_dump(DumperFn &&, substr)
{
return 0;
}
// terminates the variadic recursion
template<DumperPfn dumpfn>
size_t cat_dump(substr)
{
return 0;
}
/// @endcond
/** take the function pointer as a function argument */
template<class DumperFn, class Arg, class... Args>
size_t cat_dump(DumperFn &&dumpfn, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t size_for_a = dump(dumpfn, buf, a);
if(C4_UNLIKELY(size_for_a > buf.len))
buf = buf.first(0); // ensure no more calls
size_t size_for_more = cat_dump(dumpfn, buf, more...);
return size_for_more > size_for_a ? size_for_more : size_for_a;
}
/** take the function pointer as a template argument */
template<DumperPfn dumpfn,class Arg, class... Args>
size_t cat_dump(substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t size_for_a = dump<dumpfn>(buf, a);
if(C4_LIKELY(size_for_a > buf.len))
buf = buf.first(0); // ensure no more calls
size_t size_for_more = cat_dump<dumpfn>(buf, more...);
return size_for_more > size_for_a ? size_for_more : size_for_a;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
namespace detail {
// terminates the variadic recursion
template<DumperPfn dumpfn, class Arg>
DumpResults cat_dump_resume(size_t currarg, DumpResults results, substr buf, Arg const& C4_RESTRICT a)
{
if(C4_LIKELY(results.write_arg(currarg)))
{
size_t sz = dump<dumpfn>(buf, a); // yield to the specialized function
if(currarg == results.lastok + 1 && sz <= buf.len)
results.lastok = currarg;
results.bufsize = sz > results.bufsize ? sz : results.bufsize;
}
return results;
}
// terminates the variadic recursion
template<class DumperFn, class Arg>
DumpResults cat_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a)
{
if(C4_LIKELY(results.write_arg(currarg)))
{
size_t sz = dump(dumpfn, buf, a); // yield to the specialized function
if(currarg == results.lastok + 1 && sz <= buf.len)
results.lastok = currarg;
results.bufsize = sz > results.bufsize ? sz : results.bufsize;
}
return results;
}
template<DumperPfn dumpfn, class Arg, class... Args>
DumpResults cat_dump_resume(size_t currarg, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
results = detail::cat_dump_resume<dumpfn>(currarg, results, buf, a);
return detail::cat_dump_resume<dumpfn>(currarg + 1u, results, buf, more...);
}
template<class DumperFn, class Arg, class... Args>
DumpResults cat_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
results = detail::cat_dump_resume(currarg, dumpfn, results, buf, a);
return detail::cat_dump_resume(currarg + 1u, dumpfn, results, buf, more...);
}
} // namespace detail
/// @endcond
template<DumperPfn dumpfn, class Arg, class... Args>
C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
if(results.bufsize > buf.len)
return results;
return detail::cat_dump_resume<dumpfn>(0u, results, buf, a, more...);
}
template<class DumperFn, class Arg, class... Args>
C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumperFn &&dumpfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
if(results.bufsize > buf.len)
return results;
return detail::cat_dump_resume(0u, dumpfn, results, buf, a, more...);
}
template<DumperPfn dumpfn, class Arg, class... Args>
C4_ALWAYS_INLINE DumpResults cat_dump_resume(substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
return detail::cat_dump_resume<dumpfn>(0u, DumpResults{}, buf, a, more...);
}
template<class DumperFn, class Arg, class... Args>
C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumperFn &&dumpfn, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
return detail::cat_dump_resume(0u, dumpfn, DumpResults{}, buf, a, more...);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
// terminate the recursion
template<class DumperFn, class Sep>
size_t catsep_dump(DumperFn &&, substr, Sep const& C4_RESTRICT)
{
return 0;
}
// terminate the recursion
template<DumperPfn dumpfn, class Sep>
size_t catsep_dump(substr, Sep const& C4_RESTRICT)
{
return 0;
}
/// @endcond
/** take the function pointer as a function argument */
template<class DumperFn, class Sep, class Arg, class... Args>
size_t catsep_dump(DumperFn &&dumpfn, substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t sz = dump(dumpfn, buf, a);
if(C4_UNLIKELY(sz > buf.len))
buf = buf.first(0); // ensure no more calls
if C4_IF_CONSTEXPR (sizeof...(more) > 0)
{
size_t szsep = dump(dumpfn, buf, sep);
if(C4_UNLIKELY(szsep > buf.len))
buf = buf.first(0); // ensure no more calls
sz = sz > szsep ? sz : szsep;
}
size_t size_for_more = catsep_dump(dumpfn, buf, sep, more...);
return size_for_more > sz ? size_for_more : sz;
}
/** take the function pointer as a template argument */
template<DumperPfn dumpfn, class Sep, class Arg, class... Args>
size_t catsep_dump(substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t sz = dump<dumpfn>(buf, a);
if(C4_UNLIKELY(sz > buf.len))
buf = buf.first(0); // ensure no more calls
if C4_IF_CONSTEXPR (sizeof...(more) > 0)
{
size_t szsep = dump<dumpfn>(buf, sep);
if(C4_UNLIKELY(szsep > buf.len))
buf = buf.first(0); // ensure no more calls
sz = sz > szsep ? sz : szsep;
}
size_t size_for_more = catsep_dump<dumpfn>(buf, sep, more...);
return size_for_more > sz ? size_for_more : sz;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
namespace detail {
template<DumperPfn dumpfn, class Arg>
void catsep_dump_resume_(size_t currarg, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Arg const& C4_RESTRICT a)
{
if(C4_LIKELY(results->write_arg(currarg)))
{
size_t sz = dump<dumpfn>(*buf, a);
results->bufsize = sz > results->bufsize ? sz : results->bufsize;
if(C4_LIKELY(sz <= buf->len))
results->lastok = currarg;
else
buf->len = 0;
}
}
template<class DumperFn, class Arg>
void catsep_dump_resume_(size_t currarg, DumperFn &&dumpfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Arg const& C4_RESTRICT a)
{
if(C4_LIKELY(results->write_arg(currarg)))
{
size_t sz = dump(dumpfn, *buf, a);
results->bufsize = sz > results->bufsize ? sz : results->bufsize;
if(C4_LIKELY(sz <= buf->len))
results->lastok = currarg;
else
buf->len = 0;
}
}
template<DumperPfn dumpfn, class Sep, class Arg>
C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT, Arg const& C4_RESTRICT a)
{
detail::catsep_dump_resume_<dumpfn>(currarg, results, buf, a);
}
template<class DumperFn, class Sep, class Arg>
C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT, Arg const& C4_RESTRICT a)
{
detail::catsep_dump_resume_(currarg, dumpfn, results, buf, a);
}
template<DumperPfn dumpfn, class Sep, class Arg, class... Args>
C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
detail::catsep_dump_resume_<dumpfn>(currarg , results, buf, a);
detail::catsep_dump_resume_<dumpfn>(currarg + 1u, results, buf, sep);
detail::catsep_dump_resume <dumpfn>(currarg + 2u, results, buf, sep, more...);
}
template<class DumperFn, class Sep, class Arg, class... Args>
C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
detail::catsep_dump_resume_(currarg , dumpfn, results, buf, a);
detail::catsep_dump_resume_(currarg + 1u, dumpfn, results, buf, sep);
detail::catsep_dump_resume (currarg + 2u, dumpfn, results, buf, sep, more...);
}
} // namespace detail
/// @endcond
template<DumperPfn dumpfn, class Sep, class... Args>
C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumpResults results, substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more)
{
detail::catsep_dump_resume<dumpfn>(0u, &results, &buf, sep, more...);
return results;
}
template<class DumperFn, class Sep, class... Args>
C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumperFn &&dumpfn, DumpResults results, substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more)
{
detail::catsep_dump_resume(0u, dumpfn, &results, &buf, sep, more...);
return results;
}
template<DumperPfn dumpfn, class Sep, class... Args>
C4_ALWAYS_INLINE DumpResults catsep_dump_resume(substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more)
{
DumpResults results;
detail::catsep_dump_resume<dumpfn>(0u, &results, &buf, sep, more...);
return results;
}
template<class DumperFn, class Sep, class... Args>
C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumperFn &&dumpfn, substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more)
{
DumpResults results;
detail::catsep_dump_resume(0u, dumpfn, &results, &buf, sep, more...);
return results;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
/** take the function pointer as a function argument */
template<class DumperFn>
C4_ALWAYS_INLINE size_t format_dump(DumperFn &&dumpfn, substr buf, csubstr fmt)
{
// we can dump without using buf
// but we'll only dump if the buffer is ok
if(C4_LIKELY(buf.len > 0 && fmt.len))
dumpfn(fmt);
return 0u;
}
/** take the function pointer as a template argument */
template<DumperPfn dumpfn>
C4_ALWAYS_INLINE size_t format_dump(substr buf, csubstr fmt)
{
// we can dump without using buf
// but we'll only dump if the buffer is ok
if(C4_LIKELY(buf.len > 0 && fmt.len > 0))
dumpfn(fmt);
return 0u;
}
/// @endcond
/** take the function pointer as a function argument */
template<class DumperFn, class Arg, class... Args>
C4_NO_INLINE size_t format_dump(DumperFn &&dumpfn, substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
// we can dump without using buf
// but we'll only dump if the buffer is ok
size_t pos = fmt.find("{}"); // @todo use _find_fmt()
if(C4_UNLIKELY(pos == csubstr::npos))
{
if(C4_LIKELY(buf.len > 0 && fmt.len > 0))
dumpfn(fmt);
return 0u;
}
if(C4_LIKELY(buf.len > 0 && pos > 0))
dumpfn(fmt.first(pos)); // we can dump without using buf
fmt = fmt.sub(pos + 2); // skip {} do this before assigning to pos again
pos = dump(dumpfn, buf, a);
if(C4_UNLIKELY(pos > buf.len))
buf.len = 0; // ensure no more calls to dump
size_t size_for_more = format_dump(dumpfn, buf, fmt, more...);
return size_for_more > pos ? size_for_more : pos;
}
/** take the function pointer as a template argument */
template<DumperPfn dumpfn, class Arg, class... Args>
C4_NO_INLINE size_t format_dump(substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
// we can dump without using buf
// but we'll only dump if the buffer is ok
size_t pos = fmt.find("{}"); // @todo use _find_fmt()
if(C4_UNLIKELY(pos == csubstr::npos))
{
if(C4_LIKELY(buf.len > 0 && fmt.len > 0))
dumpfn(fmt);
return 0u;
}
if(C4_LIKELY(buf.len > 0 && pos > 0))
dumpfn(fmt.first(pos)); // we can dump without using buf
fmt = fmt.sub(pos + 2); // skip {} do this before assigning to pos again
pos = dump<dumpfn>(buf, a);
if(C4_UNLIKELY(pos > buf.len))
buf.len = 0; // ensure no more calls to dump
size_t size_for_more = format_dump<dumpfn>(buf, fmt, more...);
return size_for_more > pos ? size_for_more : pos;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
namespace detail {
template<DumperPfn dumpfn>
DumpResults format_dump_resume(size_t currarg, DumpResults results, substr buf, csubstr fmt)
{
// we can dump without using buf
// but we'll only dump if the buffer is ok
if(C4_LIKELY(buf.len > 0))
{
dumpfn(fmt);
results.lastok = currarg;
}
return results;
}
template<class DumperFn>
DumpResults format_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, csubstr fmt)
{
// we can dump without using buf
// but we'll only dump if the buffer is ok
if(C4_LIKELY(buf.len > 0))
{
dumpfn(fmt);
results.lastok = currarg;
}
return results;
}
template<DumperPfn dumpfn, class Arg, class... Args>
DumpResults format_dump_resume(size_t currarg, DumpResults results, substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
// we need to process the format even if we're not
// going to print the first arguments because we're resuming
size_t pos = fmt.find("{}"); // @todo use _find_fmt()
// we can dump without using buf
// but we'll only dump if the buffer is ok
if(C4_LIKELY(results.write_arg(currarg)))
{
if(C4_UNLIKELY(pos == csubstr::npos))
{
if(C4_LIKELY(buf.len > 0))
{
results.lastok = currarg;
dumpfn(fmt);
}
return results;
}
if(C4_LIKELY(buf.len > 0))
{
results.lastok = currarg;
dumpfn(fmt.first(pos));
}
}
fmt = fmt.sub(pos + 2);
if(C4_LIKELY(results.write_arg(currarg + 1)))
{
pos = dump<dumpfn>(buf, a);
results.bufsize = pos > results.bufsize ? pos : results.bufsize;
if(C4_LIKELY(pos <= buf.len))
results.lastok = currarg + 1;
else
buf.len = 0;
}
return detail::format_dump_resume<dumpfn>(currarg + 2u, results, buf, fmt, more...);
}
/// @endcond
template<class DumperFn, class Arg, class... Args>
DumpResults format_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
// we need to process the format even if we're not
// going to print the first arguments because we're resuming
size_t pos = fmt.find("{}"); // @todo use _find_fmt()
// we can dump without using buf
// but we'll only dump if the buffer is ok
if(C4_LIKELY(results.write_arg(currarg)))
{
if(C4_UNLIKELY(pos == csubstr::npos))
{
if(C4_LIKELY(buf.len > 0))
{
results.lastok = currarg;
dumpfn(fmt);
}
return results;
}
if(C4_LIKELY(buf.len > 0))
{
results.lastok = currarg;
dumpfn(fmt.first(pos));
}
}
fmt = fmt.sub(pos + 2);
if(C4_LIKELY(results.write_arg(currarg + 1)))
{
pos = dump(dumpfn, buf, a);
results.bufsize = pos > results.bufsize ? pos : results.bufsize;
if(C4_LIKELY(pos <= buf.len))
results.lastok = currarg + 1;
else
buf.len = 0;
}
return detail::format_dump_resume(currarg + 2u, dumpfn, results, buf, fmt, more...);
}
} // namespace detail
template<DumperPfn dumpfn, class... Args>
C4_ALWAYS_INLINE DumpResults format_dump_resume(DumpResults results, substr buf, csubstr fmt, Args const& C4_RESTRICT ...more)
{
return detail::format_dump_resume<dumpfn>(0u, results, buf, fmt, more...);
}
template<class DumperFn, class... Args>
C4_ALWAYS_INLINE DumpResults format_dump_resume(DumperFn &&dumpfn, DumpResults results, substr buf, csubstr fmt, Args const& C4_RESTRICT ...more)
{
return detail::format_dump_resume(0u, dumpfn, results, buf, fmt, more...);
}
template<DumperPfn dumpfn, class... Args>
C4_ALWAYS_INLINE DumpResults format_dump_resume(substr buf, csubstr fmt, Args const& C4_RESTRICT ...more)
{
return detail::format_dump_resume<dumpfn>(0u, DumpResults{}, buf, fmt, more...);
}
template<class DumperFn, class... Args>
C4_ALWAYS_INLINE DumpResults format_dump_resume(DumperFn &&dumpfn, substr buf, csubstr fmt, Args const& C4_RESTRICT ...more)
{
return detail::format_dump_resume(0u, dumpfn, DumpResults{}, buf, fmt, more...);
}
C4_SUPPRESS_WARNING_GCC_CLANG_POP
} // namespace c4
#endif /* C4_DUMP_HPP_ */

435
3rdparty/rapidyaml/include/c4/error.hpp vendored Normal file
View File

@ -0,0 +1,435 @@
#ifndef _C4_ERROR_HPP_
#define _C4_ERROR_HPP_
/** @file error.hpp Facilities for error reporting and runtime assertions. */
/** @defgroup error_checking Error checking */
#include "c4/config.hpp"
#ifdef _DOXYGEN_
/** if this is defined and exceptions are enabled, then calls to C4_ERROR()
* will throw an exception
* @ingroup error_checking */
# define C4_EXCEPTIONS_ENABLED
/** if this is defined and exceptions are enabled, then calls to C4_ERROR()
* will throw an exception
* @see C4_EXCEPTIONS_ENABLED
* @ingroup error_checking */
# define C4_ERROR_THROWS_EXCEPTION
/** evaluates to noexcept when C4_ERROR might be called and
* exceptions are disabled. Otherwise, defaults to nothing.
* @ingroup error_checking */
# define C4_NOEXCEPT
#endif // _DOXYGEN_
#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION)
# define C4_NOEXCEPT
#else
# define C4_NOEXCEPT noexcept
#endif
namespace c4 {
namespace detail {
struct fail_type__ {};
} // detail
} // c4
#define C4_STATIC_ERROR(dummy_type, errmsg) \
static_assert(std::is_same<dummy_type, c4::detail::fail_type__>::value, errmsg)
//-----------------------------------------------------------------------------
#define C4_ASSERT_SAME_TYPE(ty1, ty2) \
C4_STATIC_ASSERT(std::is_same<ty1 C4_COMMA_X ty2>::value)
#define C4_ASSERT_DIFF_TYPE(ty1, ty2) \
C4_STATIC_ASSERT( ! std::is_same<ty1 C4_COMMA_X ty2>::value)
//-----------------------------------------------------------------------------
#ifdef _DOXYGEN_
/** utility macro that triggers a breakpoint when
* the debugger is attached and NDEBUG is not defined.
* @ingroup error_checking */
# define C4_DEBUG_BREAK()
#endif // _DOXYGEN_
#if defined(NDEBUG) || defined(C4_NO_DEBUG_BREAK)
# define C4_DEBUG_BREAK()
#else
# ifdef __clang__
# pragma clang diagnostic push
# if !defined(__APPLE_CC__)
# if __clang_major__ >= 10
# pragma clang diagnostic ignored "-Wgnu-inline-cpp-without-extern" // debugbreak/debugbreak.h:50:16: error: 'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10 [-Werror,-Wgnu-inline-cpp-without-extern]
# endif
# else
# if __clang_major__ >= 13
# pragma clang diagnostic ignored "-Wgnu-inline-cpp-without-extern" // debugbreak/debugbreak.h:50:16: error: 'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10 [-Werror,-Wgnu-inline-cpp-without-extern]
# endif
# endif
# elif defined(__GNUC__)
# endif
# include <c4/ext/debugbreak/debugbreak.h>
# define C4_DEBUG_BREAK() if(c4::is_debugger_attached()) { ::debug_break(); }
# ifdef __clang__
# pragma clang diagnostic pop
# elif defined(__GNUC__)
# endif
#endif
namespace c4 {
C4CORE_EXPORT bool is_debugger_attached();
} // namespace c4
//-----------------------------------------------------------------------------
#ifdef __clang__
/* NOTE: using , ## __VA_ARGS__ to deal with zero-args calls to
* variadic macros is not portable, but works in clang, gcc, msvc, icc.
* clang requires switching off compiler warnings for pedantic mode.
* @see http://stackoverflow.com/questions/32047685/variadic-macro-without-arguments */
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" // warning: token pasting of ',' and __VA_ARGS__ is a GNU extension
#elif defined(__GNUC__)
/* GCC also issues a warning for zero-args calls to variadic macros.
* This warning is switched on with -pedantic and apparently there is no
* easy way to turn it off as with clang. But marking this as a system
* header works.
* @see https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html
* @see http://stackoverflow.com/questions/35587137/ */
# pragma GCC system_header
#endif
//-----------------------------------------------------------------------------
namespace c4 {
typedef enum : uint32_t {
/** when an error happens and the debugger is attached, call C4_DEBUG_BREAK().
* Without effect otherwise. */
ON_ERROR_DEBUGBREAK = 0x01 << 0,
/** when an error happens log a message. */
ON_ERROR_LOG = 0x01 << 1,
/** when an error happens invoke a callback if it was set with
* set_error_callback(). */
ON_ERROR_CALLBACK = 0x01 << 2,
/** when an error happens call std::terminate(). */
ON_ERROR_ABORT = 0x01 << 3,
/** when an error happens and exceptions are enabled throw an exception.
* Without effect otherwise. */
ON_ERROR_THROW = 0x01 << 4,
/** the default flags. */
ON_ERROR_DEFAULTS = ON_ERROR_DEBUGBREAK|ON_ERROR_LOG|ON_ERROR_CALLBACK|ON_ERROR_ABORT
} ErrorFlags_e;
using error_flags = uint32_t;
C4CORE_EXPORT void set_error_flags(error_flags f);
C4CORE_EXPORT error_flags get_error_flags();
using error_callback_type = void (*)(const char* msg, size_t msg_size);
C4CORE_EXPORT void set_error_callback(error_callback_type cb);
C4CORE_EXPORT error_callback_type get_error_callback();
//-----------------------------------------------------------------------------
/** RAII class controling the error settings inside a scope. */
struct ScopedErrorSettings
{
error_flags m_flags;
error_callback_type m_callback;
explicit ScopedErrorSettings(error_callback_type cb)
: m_flags(get_error_flags()),
m_callback(get_error_callback())
{
set_error_callback(cb);
}
explicit ScopedErrorSettings(error_flags flags)
: m_flags(get_error_flags()),
m_callback(get_error_callback())
{
set_error_flags(flags);
}
explicit ScopedErrorSettings(error_flags flags, error_callback_type cb)
: m_flags(get_error_flags()),
m_callback(get_error_callback())
{
set_error_flags(flags);
set_error_callback(cb);
}
~ScopedErrorSettings()
{
set_error_flags(m_flags);
set_error_callback(m_callback);
}
};
//-----------------------------------------------------------------------------
/** source location */
struct srcloc;
C4CORE_EXPORT void handle_error(srcloc s, const char *fmt, ...);
C4CORE_EXPORT void handle_warning(srcloc s, const char *fmt, ...);
# define C4_ERROR(msg, ...) \
do { \
if(c4::get_error_flags() & c4::ON_ERROR_DEBUGBREAK) \
{ \
C4_DEBUG_BREAK() \
} \
c4::handle_error(C4_SRCLOC(), msg, ## __VA_ARGS__); \
} while(0)
# define C4_WARNING(msg, ...) \
c4::handle_warning(C4_SRCLOC(), msg, ## __VA_ARGS__)
#if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC)
struct srcloc
{
const char *file = "";
const char *func = "";
int line = 0;
};
#define C4_SRCLOC() c4::srcloc{__FILE__, C4_PRETTY_FUNC, __LINE__}
#elif defined(C4_ERROR_SHOWS_FILELINE)
struct srcloc
{
const char *file;
int line;
};
#define C4_SRCLOC() c4::srcloc{__FILE__, __LINE__}
#elif ! defined(C4_ERROR_SHOWS_FUNC)
struct srcloc
{
};
#define C4_SRCLOC() c4::srcloc()
#else
# error not implemented
#endif
//-----------------------------------------------------------------------------
// assertions
// Doxygen needs this so that only one definition counts
#ifdef _DOXYGEN_
/** Explicitly enables assertions, independently of NDEBUG status.
* This is meant to allow enabling assertions even when NDEBUG is defined.
* Defaults to undefined.
* @ingroup error_checking */
# define C4_USE_ASSERT
/** assert that a condition is true; this is turned off when NDEBUG
* is defined and C4_USE_ASSERT is not true.
* @ingroup error_checking */
# define C4_ASSERT
/** same as C4_ASSERT(), additionally prints a printf-formatted message
* @ingroup error_checking */
# define C4_ASSERT_MSG
/** evaluates to C4_NOEXCEPT when C4_XASSERT is disabled; otherwise, defaults
* to noexcept
* @ingroup error_checking */
# define C4_NOEXCEPT_A
#endif // _DOXYGEN_
#ifndef C4_USE_ASSERT
# ifdef NDEBUG
# define C4_USE_ASSERT 0
# else
# define C4_USE_ASSERT 1
# endif
#endif
#if C4_USE_ASSERT
# define C4_ASSERT(cond) C4_CHECK(cond)
# define C4_ASSERT_MSG(cond, /*fmt, */...) C4_CHECK_MSG(cond, ## __VA_ARGS__)
# define C4_ASSERT_IF(predicate, cond) if(predicate) { C4_ASSERT(cond); }
# define C4_NOEXCEPT_A C4_NOEXCEPT
#else
# define C4_ASSERT(cond)
# define C4_ASSERT_MSG(cond, /*fmt, */...)
# define C4_ASSERT_IF(predicate, cond)
# define C4_NOEXCEPT_A noexcept
#endif
//-----------------------------------------------------------------------------
// extreme assertions
// Doxygen needs this so that only one definition counts
#ifdef _DOXYGEN_
/** Explicitly enables extreme assertions; this is meant to allow enabling
* assertions even when NDEBUG is defined. Defaults to undefined.
* @ingroup error_checking */
# define C4_USE_XASSERT
/** extreme assertion: can be switched off independently of
* the regular assertion; use for example for bounds checking in hot code.
* Turned on only when C4_USE_XASSERT is defined
* @ingroup error_checking */
# define C4_XASSERT
/** same as C4_XASSERT(), and additionally prints a printf-formatted message
* @ingroup error_checking */
# define C4_XASSERT_MSG
/** evaluates to C4_NOEXCEPT when C4_XASSERT is disabled; otherwise, defaults to noexcept
* @ingroup error_checking */
# define C4_NOEXCEPT_X
#endif // _DOXYGEN_
#ifndef C4_USE_XASSERT
# define C4_USE_XASSERT C4_USE_ASSERT
#endif
#if C4_USE_XASSERT
# define C4_XASSERT(cond) C4_CHECK(cond)
# define C4_XASSERT_MSG(cond, /*fmt, */...) C4_CHECK_MSG(cond, ## __VA_ARGS__)
# define C4_XASSERT_IF(predicate, cond) if(predicate) { C4_XASSERT(cond); }
# define C4_NOEXCEPT_X C4_NOEXCEPT
#else
# define C4_XASSERT(cond)
# define C4_XASSERT_MSG(cond, /*fmt, */...)
# define C4_XASSERT_IF(predicate, cond)
# define C4_NOEXCEPT_X noexcept
#endif
//-----------------------------------------------------------------------------
// checks: never switched-off
/** Check that a condition is true, or raise an error when not
* true. Unlike C4_ASSERT(), this check is not disabled in non-debug
* builds.
* @see C4_ASSERT
* @ingroup error_checking
*
* @todo add constexpr-compatible compile-time assert:
* https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/
*/
#define C4_CHECK(cond) \
do { \
if(C4_UNLIKELY(!(cond))) \
{ \
C4_ERROR("check failed: %s", #cond); \
} \
} while(0)
/** like C4_CHECK(), and additionally log a printf-style message.
* @see C4_CHECK
* @ingroup error_checking */
#define C4_CHECK_MSG(cond, fmt, ...) \
do { \
if(C4_UNLIKELY(!(cond))) \
{ \
C4_ERROR("check failed: " #cond "\n" fmt, ## __VA_ARGS__); \
} \
} while(0)
//-----------------------------------------------------------------------------
// Common error conditions
#define C4_NOT_IMPLEMENTED() C4_ERROR("NOT IMPLEMENTED")
#define C4_NOT_IMPLEMENTED_MSG(/*msg, */...) C4_ERROR("NOT IMPLEMENTED: " __VA_ARGS__)
#define C4_NOT_IMPLEMENTED_IF(condition) do { if(C4_UNLIKELY(condition)) { C4_ERROR("NOT IMPLEMENTED"); } } while(0)
#define C4_NOT_IMPLEMENTED_IF_MSG(condition, /*msg, */...) do { if(C4_UNLIKELY(condition)) { C4_ERROR("NOT IMPLEMENTED: " __VA_ARGS__); } } while(0)
#define C4_NEVER_REACH() do { C4_ERROR("never reach this point"); C4_UNREACHABLE(); } while(0)
#define C4_NEVER_REACH_MSG(/*msg, */...) do { C4_ERROR("never reach this point: " __VA_ARGS__); C4_UNREACHABLE(); } while(0)
//-----------------------------------------------------------------------------
// helpers for warning suppression
// idea adapted from https://github.com/onqtam/doctest/
// TODO: add C4_MESSAGE() https://stackoverflow.com/questions/18252351/custom-preprocessor-macro-for-a-conditional-pragma-message-xxx?rq=1
#ifdef C4_MSVC
#define C4_SUPPRESS_WARNING_MSVC_PUSH __pragma(warning(push))
#define C4_SUPPRESS_WARNING_MSVC(w) __pragma(warning(disable : w))
#define C4_SUPPRESS_WARNING_MSVC_POP __pragma(warning(pop))
#else // C4_MSVC
#define C4_SUPPRESS_WARNING_MSVC_PUSH
#define C4_SUPPRESS_WARNING_MSVC(w)
#define C4_SUPPRESS_WARNING_MSVC_POP
#endif // C4_MSVC
#ifdef C4_CLANG
#define C4_PRAGMA_TO_STR(x) _Pragma(#x)
#define C4_SUPPRESS_WARNING_CLANG_PUSH _Pragma("clang diagnostic push")
#define C4_SUPPRESS_WARNING_CLANG(w) C4_PRAGMA_TO_STR(clang diagnostic ignored w)
#define C4_SUPPRESS_WARNING_CLANG_POP _Pragma("clang diagnostic pop")
#else // C4_CLANG
#define C4_SUPPRESS_WARNING_CLANG_PUSH
#define C4_SUPPRESS_WARNING_CLANG(w)
#define C4_SUPPRESS_WARNING_CLANG_POP
#endif // C4_CLANG
#ifdef C4_GCC
#define C4_PRAGMA_TO_STR(x) _Pragma(#x)
#define C4_SUPPRESS_WARNING_GCC_PUSH _Pragma("GCC diagnostic push")
#define C4_SUPPRESS_WARNING_GCC(w) C4_PRAGMA_TO_STR(GCC diagnostic ignored w)
#define C4_SUPPRESS_WARNING_GCC_POP _Pragma("GCC diagnostic pop")
#else // C4_GCC
#define C4_SUPPRESS_WARNING_GCC_PUSH
#define C4_SUPPRESS_WARNING_GCC(w)
#define C4_SUPPRESS_WARNING_GCC_POP
#endif // C4_GCC
#define C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(w) \
C4_SUPPRESS_WARNING_MSVC_PUSH \
C4_SUPPRESS_WARNING_MSVC(w)
#define C4_SUPPRESS_WARNING_CLANG_WITH_PUSH(w) \
C4_SUPPRESS_WARNING_CLANG_PUSH \
C4_SUPPRESS_WARNING_CLANG(w)
#define C4_SUPPRESS_WARNING_GCC_WITH_PUSH(w) \
C4_SUPPRESS_WARNING_GCC_PUSH \
C4_SUPPRESS_WARNING_GCC(w)
#define C4_SUPPRESS_WARNING_GCC_CLANG_PUSH \
C4_SUPPRESS_WARNING_GCC_PUSH \
C4_SUPPRESS_WARNING_CLANG_PUSH
#define C4_SUPPRESS_WARNING_GCC_CLANG(w) \
C4_SUPPRESS_WARNING_GCC(w) \
C4_SUPPRESS_WARNING_CLANG(w)
#define C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH(w) \
C4_SUPPRESS_WARNING_GCC_WITH_PUSH(w) \
C4_SUPPRESS_WARNING_CLANG_WITH_PUSH(w)
#define C4_SUPPRESS_WARNING_GCC_CLANG_POP \
C4_SUPPRESS_WARNING_GCC_POP \
C4_SUPPRESS_WARNING_CLANG_POP
} // namespace c4
#ifdef __clang__
# pragma clang diagnostic pop
#endif
#endif /* _C4_ERROR_HPP_ */

View File

@ -0,0 +1,18 @@
#ifndef C4_EXPORT_HPP_
#define C4_EXPORT_HPP_
#ifdef _WIN32
#ifdef C4CORE_SHARED
#ifdef C4CORE_EXPORTS
#define C4CORE_EXPORT __declspec(dllexport)
#else
#define C4CORE_EXPORT __declspec(dllimport)
#endif
#else
#define C4CORE_EXPORT
#endif
#else
#define C4CORE_EXPORT
#endif
#endif /* C4CORE_EXPORT_HPP_ */

1049
3rdparty/rapidyaml/include/c4/format.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,358 @@
#ifndef _C4_LANGUAGE_HPP_
#define _C4_LANGUAGE_HPP_
/** @file language.hpp Provides language standard information macros and
* compiler agnostic utility macros: namespace facilities, function attributes,
* variable attributes, etc.
* @ingroup basic_headers */
#include "c4/preprocessor.hpp"
#include "c4/compiler.hpp"
/* Detect C++ standard.
* @see http://stackoverflow.com/a/7132549/5875572 */
#ifndef C4_CPP
# if defined(_MSC_VER) && !defined(__clang__)
# if _MSC_VER >= 1910 // >VS2015: VS2017, VS2019, VS2022
# if (!defined(_MSVC_LANG))
# error _MSVC not defined
# endif
# if _MSVC_LANG >= 201705L
# define C4_CPP 20
# define C4_CPP20
# elif _MSVC_LANG == 201703L
# define C4_CPP 17
# define C4_CPP17
# elif _MSVC_LANG >= 201402L
# define C4_CPP 14
# define C4_CPP14
# elif _MSVC_LANG >= 201103L
# define C4_CPP 11
# define C4_CPP11
# else
# error C++ lesser than C++11 not supported
# endif
# else
# if _MSC_VER == 1900
# define C4_CPP 14 // VS2015 is c++14 https://devblogs.microsoft.com/cppblog/c111417-features-in-vs-2015-rtm/
# define C4_CPP14
# elif _MSC_VER == 1800 // VS2013
# define C4_CPP 11
# define C4_CPP11
# else
# error C++ lesser than C++11 not supported
# endif
# endif
# elif defined(__INTEL_COMPILER) // https://software.intel.com/en-us/node/524490
# ifdef __INTEL_CXX20_MODE__ // not sure about this
# define C4_CPP 20
# define C4_CPP20
# elif defined __INTEL_CXX17_MODE__ // not sure about this
# define C4_CPP 17
# define C4_CPP17
# elif defined __INTEL_CXX14_MODE__ // not sure about this
# define C4_CPP 14
# define C4_CPP14
# elif defined __INTEL_CXX11_MODE__
# define C4_CPP 11
# define C4_CPP11
# else
# error C++ lesser than C++11 not supported
# endif
# else
# ifndef __cplusplus
# error __cplusplus is not defined?
# endif
# if __cplusplus == 1
# error cannot handle __cplusplus==1
# elif __cplusplus >= 201709L
# define C4_CPP 20
# define C4_CPP20
# elif __cplusplus >= 201703L
# define C4_CPP 17
# define C4_CPP17
# elif __cplusplus >= 201402L
# define C4_CPP 14
# define C4_CPP14
# elif __cplusplus >= 201103L
# define C4_CPP 11
# define C4_CPP11
# elif __cplusplus >= 199711L
# error C++ lesser than C++11 not supported
# endif
# endif
#else
# ifdef C4_CPP == 20
# define C4_CPP20
# elif C4_CPP == 17
# define C4_CPP17
# elif C4_CPP == 14
# define C4_CPP14
# elif C4_CPP == 11
# define C4_CPP11
# elif C4_CPP == 98
# define C4_CPP98
# error C++ lesser than C++11 not supported
# else
# error C4_CPP must be one of 20, 17, 14, 11, 98
# endif
#endif
#ifdef C4_CPP20
# define C4_CPP17
# define C4_CPP14
# define C4_CPP11
#elif defined(C4_CPP17)
# define C4_CPP14
# define C4_CPP11
#elif defined(C4_CPP14)
# define C4_CPP11
#endif
/** lifted from this answer: http://stackoverflow.com/a/20170989/5875572 */
#if defined(_MSC_VER) && !defined(__clang__)
# if _MSC_VER < 1900
# define C4_CONSTEXPR11
# define C4_CONSTEXPR14
# elif _MSC_VER < 2000
# define C4_CONSTEXPR11 constexpr
# define C4_CONSTEXPR14
# else
# define C4_CONSTEXPR11 constexpr
# define C4_CONSTEXPR14 constexpr
# endif
#else
# if __cplusplus < 201103
# define C4_CONSTEXPR11
# define C4_CONSTEXPR14
# elif __cplusplus == 201103
# define C4_CONSTEXPR11 constexpr
# define C4_CONSTEXPR14
# else
# define C4_CONSTEXPR11 constexpr
# define C4_CONSTEXPR14 constexpr
# endif
#endif // _MSC_VER
#if C4_CPP < 17
#define C4_IF_CONSTEXPR
#define C4_INLINE_CONSTEXPR constexpr
#else
#define C4_IF_CONSTEXPR constexpr
#define C4_INLINE_CONSTEXPR inline constexpr
#endif
#if defined(_MSC_VER) && !defined(__clang__)
# if (defined(_CPPUNWIND) && (_CPPUNWIND == 1))
# define C4_EXCEPTIONS
# endif
#else
# if defined(__EXCEPTIONS) || defined(__cpp_exceptions)
# define C4_EXCEPTIONS
# endif
#endif
#ifdef C4_EXCEPTIONS
# define C4_IF_EXCEPTIONS_(exc_code, setjmp_code) exc_code
# define C4_IF_EXCEPTIONS(exc_code, setjmp_code) do { exc_code } while(0)
#else
# define C4_IF_EXCEPTIONS_(exc_code, setjmp_code) setjmp_code
# define C4_IF_EXCEPTIONS(exc_code, setjmp_code) do { setjmp_code } while(0)
#endif
#if defined(_MSC_VER) && !defined(__clang__)
# if defined(_CPPRTTI)
# define C4_RTTI
# endif
#else
# if defined(__GXX_RTTI)
# define C4_RTTI
# endif
#endif
#ifdef C4_RTTI
# define C4_IF_RTTI_(code_rtti, code_no_rtti) code_rtti
# define C4_IF_RTTI(code_rtti, code_no_rtti) do { code_rtti } while(0)
#else
# define C4_IF_RTTI_(code_rtti, code_no_rtti) code_no_rtti
# define C4_IF_RTTI(code_rtti, code_no_rtti) do { code_no_rtti } while(0)
#endif
//------------------------------------------------------------
#define _C4_BEGIN_NAMESPACE(ns) namespace ns {
#define _C4_END_NAMESPACE(ns) }
// MSVC cant handle the C4_FOR_EACH macro... need to fix this
//#define C4_BEGIN_NAMESPACE(...) C4_FOR_EACH_SEP(_C4_BEGIN_NAMESPACE, , __VA_ARGS__)
//#define C4_END_NAMESPACE(...) C4_FOR_EACH_SEP(_C4_END_NAMESPACE, , __VA_ARGS__)
#define C4_BEGIN_NAMESPACE(ns) namespace ns {
#define C4_END_NAMESPACE(ns) }
#define C4_BEGIN_HIDDEN_NAMESPACE namespace /*hidden*/ {
#define C4_END_HIDDEN_NAMESPACE } /* namespace hidden */
//------------------------------------------------------------
#ifndef C4_API
# if defined(_MSC_VER) && !defined(__clang__)
# if defined(C4_EXPORT)
# define C4_API __declspec(dllexport)
# elif defined(C4_IMPORT)
# define C4_API __declspec(dllimport)
# else
# define C4_API
# endif
# else
# define C4_API
# endif
#endif
#if defined(_MSC_VER) && !defined(__clang__)
# define C4_RESTRICT __restrict
# define C4_RESTRICT_FN __declspec(restrict)
# define C4_NO_INLINE __declspec(noinline)
# define C4_ALWAYS_INLINE inline __forceinline
/** these are not available in VS AFAIK */
# define C4_CONST
# define C4_PURE
# define C4_FLATTEN
# define C4_HOT /** @todo */
# define C4_COLD /** @todo */
# define C4_ASSUME(...) __assume(__VA_ARGS__)
# define C4_EXPECT(x, y) x /** @todo */
# define C4_LIKELY(x) x
# define C4_UNLIKELY(x) x
# define C4_UNREACHABLE() _c4_msvc_unreachable()
# define C4_ATTR_FORMAT(...) /** */
# define C4_NORETURN [[noreturn]]
# if _MSC_VER >= 1700 // VS2012
# define C4_NODISCARD _Check_return_
# else
# define C4_NODISCARD
# endif
[[noreturn]] __forceinline void _c4_msvc_unreachable() { __assume(false); } ///< https://stackoverflow.com/questions/60802864/emulating-gccs-builtin-unreachable-in-visual-studio
# define C4_UNREACHABLE_AFTER_ERR() /* */
#else
///< @todo assuming gcc-like compiler. check it is actually so.
/** for function attributes in GCC,
* @see https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes */
/** for __builtin functions in GCC,
* @see https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html */
# define C4_RESTRICT __restrict__
# define C4_RESTRICT_FN __attribute__((restrict))
# define C4_NO_INLINE __attribute__((noinline))
# define C4_ALWAYS_INLINE inline __attribute__((always_inline))
# define C4_CONST __attribute__((const))
# define C4_PURE __attribute__((pure))
/** force inlining of every callee function */
# define C4_FLATTEN __atribute__((flatten))
/** mark a function as hot, ie as having a visible impact in CPU time
* thus making it more likely to inline, etc
* @see http://stackoverflow.com/questions/15028990/semantics-of-gcc-hot-attribute */
# define C4_HOT __attribute__((hot))
/** mark a function as cold, ie as NOT having a visible impact in CPU time
* @see http://stackoverflow.com/questions/15028990/semantics-of-gcc-hot-attribute */
# define C4_COLD __attribute__((cold))
# define C4_EXPECT(x, y) __builtin_expect(x, y) ///< @see https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
# define C4_LIKELY(x) __builtin_expect(x, 1)
# define C4_UNLIKELY(x) __builtin_expect(x, 0)
# define C4_UNREACHABLE() __builtin_unreachable()
# define C4_ATTR_FORMAT(...) //__attribute__((format (__VA_ARGS__))) ///< @see https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes
# define C4_NORETURN __attribute__((noreturn))
# define C4_NODISCARD __attribute__((warn_unused_result))
# define C4_UNREACHABLE_AFTER_ERR() C4_UNREACHABLE()
// C4_ASSUME
// see https://stackoverflow.com/questions/63493968/reproducing-clangs-builtin-assume-for-gcc
// preferred option: C++ standard attribute
# ifdef __has_cpp_attribute
# if __has_cpp_attribute(assume) >= 202207L
# define C4_ASSUME(...) [[assume(__VA_ARGS__)]]
# endif
# endif
// first fallback: compiler intrinsics/attributes for assumptions
# ifndef C4_ASSUME
# if defined(__clang__)
# define C4_ASSUME(...) __builtin_assume(__VA_ARGS__)
# elif defined(__GNUC__)
# if __GNUC__ >= 13
# define C4_ASSUME(...) __attribute__((__assume__(__VA_ARGS__)))
# endif
# endif
# endif
// second fallback: possibly evaluating uses of unreachable()
// Set this to 1 if you want to allow assumptions to possibly evaluate.
# ifndef C4_ASSUME_ALLOW_EVAL
# define C4_ASSUME_ALLOW_EVAL 0
# endif
# if !defined(C4_ASSUME) && (C4_ASSUME_ALLOW_EVAL)
# define C4_ASSUME(...) do { if (!bool(__VA_ARGS__)) C4_UNREACHABLE(); ) while(0)
# endif
// last fallback: define macro as doing nothing
# ifndef C4_ASSUME
# define C4_ASSUME(...)
# endif
#endif
#if C4_CPP >= 14
# define C4_DEPRECATED(msg) [[deprecated(msg)]]
#else
# if defined(_MSC_VER)
# define C4_DEPRECATED(msg) __declspec(deprecated(msg))
# else // defined(__GNUC__) || defined(__clang__)
# define C4_DEPRECATED(msg) __attribute__((deprecated(msg)))
# endif
#endif
#ifdef _MSC_VER
# define C4_FUNC __FUNCTION__
# define C4_PRETTY_FUNC __FUNCSIG__
#else /// @todo assuming gcc-like compiler. check it is actually so.
# define C4_FUNC __FUNCTION__
# define C4_PRETTY_FUNC __PRETTY_FUNCTION__
#endif
/** prevent compiler warnings about a specific var being unused */
#define C4_UNUSED(var) (void)var
#if C4_CPP >= 17
#define C4_STATIC_ASSERT(cond) static_assert(cond)
#else
#define C4_STATIC_ASSERT(cond) static_assert((cond), #cond)
#endif
#define C4_STATIC_ASSERT_MSG(cond, msg) static_assert((cond), #cond ": " msg)
/** @def C4_DONT_OPTIMIZE idea lifted from GoogleBenchmark.
* @see https://github.com/google/benchmark/blob/master/include/benchmark/benchmark_api.h */
namespace c4 {
namespace detail {
#ifdef __GNUC__
# define C4_DONT_OPTIMIZE(var) c4::detail::dont_optimize(var)
template< class T >
C4_ALWAYS_INLINE void dont_optimize(T const& value) { asm volatile("" : : "g"(value) : "memory"); }
#else
# define C4_DONT_OPTIMIZE(var) c4::detail::use_char_pointer(reinterpret_cast< const char* >(&var))
void use_char_pointer(char const volatile*);
#endif
} // namespace detail
} // namespace c4
/** @def C4_KEEP_EMPTY_LOOP prevent an empty loop from being optimized out.
* @see http://stackoverflow.com/a/7084193/5875572 */
#if defined(_MSC_VER) && !defined(__clang__)
# define C4_KEEP_EMPTY_LOOP { char c; C4_DONT_OPTIMIZE(c); }
#else
# define C4_KEEP_EMPTY_LOOP { asm(""); }
#endif
/** @def C4_VA_LIST_REUSE_MUST_COPY
* @todo <jpmag> I strongly suspect that this is actually only in UNIX platforms. revisit this. */
#ifdef __GNUC__
# define C4_VA_LIST_REUSE_MUST_COPY
#endif
#endif /* _C4_LANGUAGE_HPP_ */

View File

@ -0,0 +1,778 @@
#ifndef _C4_MEMORY_UTIL_HPP_
#define _C4_MEMORY_UTIL_HPP_
#include "c4/config.hpp"
#include "c4/error.hpp"
#include "c4/compiler.hpp"
#include "c4/cpu.hpp"
#ifdef C4_MSVC
#include <intrin.h>
#endif
#include <string.h>
#if (defined(__GNUC__) && __GNUC__ >= 10) || defined(__has_builtin)
#define _C4_USE_LSB_INTRINSIC(which) __has_builtin(which)
#define _C4_USE_MSB_INTRINSIC(which) __has_builtin(which)
#elif defined(C4_MSVC)
#define _C4_USE_LSB_INTRINSIC(which) true
#define _C4_USE_MSB_INTRINSIC(which) true
#else
// let's try our luck
#define _C4_USE_LSB_INTRINSIC(which) true
#define _C4_USE_MSB_INTRINSIC(which) true
#endif
/** @file memory_util.hpp Some memory utilities. */
namespace c4 {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
/** set the given memory to zero */
C4_ALWAYS_INLINE void mem_zero(void* mem, size_t num_bytes)
{
memset(mem, 0, num_bytes);
}
/** set the given memory to zero */
template<class T>
C4_ALWAYS_INLINE void mem_zero(T* mem, size_t num_elms)
{
memset(mem, 0, sizeof(T) * num_elms);
}
/** set the given memory to zero */
template<class T>
C4_ALWAYS_INLINE void mem_zero(T* mem)
{
memset(mem, 0, sizeof(T));
}
C4_ALWAYS_INLINE C4_CONST bool mem_overlaps(void const* a, void const* b, size_t sza, size_t szb)
{
// thanks @timwynants
return (((const char*)b + szb) > a && b < ((const char*)a+sza));
}
void mem_repeat(void* dest, void const* pattern, size_t pattern_size, size_t num_times);
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template<class T>
C4_ALWAYS_INLINE C4_CONST bool is_aligned(T *ptr, uintptr_t alignment=alignof(T))
{
return (uintptr_t(ptr) & (alignment - uintptr_t(1))) == uintptr_t(0);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// least significant bit
/** @name msb Compute the least significant bit
* @note the input value must be nonzero
* @note the input type must be unsigned
*/
/** @{ */
// https://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightLinear
#define _c4_lsb_fallback \
unsigned c = 0; \
v = (v ^ (v - 1)) >> 1; /* Set v's trailing 0s to 1s and zero rest */ \
for(; v; ++c) \
v >>= 1; \
return (unsigned) c
// u8
template<class I>
C4_CONSTEXPR14
auto lsb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 1u, unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_LSB_INTRINSIC(__builtin_ctz)
// upcast to use the intrinsic, it's cheaper.
#ifdef C4_MSVC
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanForward(&bit, (unsigned long)v);
return bit;
#else
_c4_lsb_fallback;
#endif
#else
return (unsigned)__builtin_ctz((unsigned)v);
#endif
#else
_c4_lsb_fallback;
#endif
}
// u16
template<class I>
C4_CONSTEXPR14
auto lsb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 2u, unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_LSB_INTRINSIC(__builtin_ctz)
// upcast to use the intrinsic, it's cheaper.
// Then remember that the upcast makes it to 31bits
#ifdef C4_MSVC
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanForward(&bit, (unsigned long)v);
return bit;
#else
_c4_lsb_fallback;
#endif
#else
return (unsigned)__builtin_ctz((unsigned)v);
#endif
#else
_c4_lsb_fallback;
#endif
}
// u32
template<class I>
C4_CONSTEXPR14
auto lsb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 4u, unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_LSB_INTRINSIC(__builtin_ctz)
#ifdef C4_MSVC
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanForward(&bit, v);
return bit;
#else
_c4_lsb_fallback;
#endif
#else
return (unsigned)__builtin_ctz((unsigned)v);
#endif
#else
_c4_lsb_fallback;
#endif
}
// u64 in 64bits
template<class I>
C4_CONSTEXPR14
auto lsb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long) == 8u, unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_LSB_INTRINSIC(__builtin_ctzl)
#if defined(C4_MSVC)
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanForward64(&bit, v);
return bit;
#else
_c4_lsb_fallback;
#endif
#else
return (unsigned)__builtin_ctzl((unsigned long)v);
#endif
#else
_c4_lsb_fallback;
#endif
}
// u64 in 32bits
template<class I>
C4_CONSTEXPR14
auto lsb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long long) == 8u && sizeof(unsigned long) != sizeof(unsigned long long), unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_LSB_INTRINSIC(__builtin_ctzll)
#if defined(C4_MSVC)
#if !defined(C4_CPU_X86) && !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanForward64(&bit, v);
return bit;
#else
_c4_lsb_fallback;
#endif
#else
return (unsigned)__builtin_ctzll((unsigned long long)v);
#endif
#else
_c4_lsb_fallback;
#endif
}
#undef _c4_lsb_fallback
/** @} */
namespace detail {
template<class I, I val, unsigned num_bits, bool finished> struct _lsb11;
template<class I, I val, unsigned num_bits>
struct _lsb11<I, val, num_bits, false>
{
enum : unsigned { num = _lsb11<I, (val>>1), num_bits+I(1), (((val>>1)&I(1))!=I(0))>::num };
};
template<class I, I val, unsigned num_bits>
struct _lsb11<I, val, num_bits, true>
{
enum : unsigned { num = num_bits };
};
} // namespace detail
/** TMP version of lsb(); this needs to be implemented with template
* meta-programming because C++11 cannot use a constexpr function with
* local variables
* @see lsb */
template<class I, I number>
struct lsb11
{
static_assert(number != 0, "lsb: number must be nonzero");
enum : unsigned { value = detail::_lsb11<I, number, 0, ((number&I(1))!=I(0))>::num};
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// most significant bit
/** @name msb Compute the most significant bit
* @note the input value must be nonzero
* @note the input type must be unsigned
*/
/** @{ */
#define _c4_msb8_fallback \
unsigned n = 0; \
if(v & I(0xf0)) v >>= 4, n |= I(4); \
if(v & I(0x0c)) v >>= 2, n |= I(2); \
if(v & I(0x02)) v >>= 1, n |= I(1); \
return n
#define _c4_msb16_fallback \
unsigned n = 0; \
if(v & I(0xff00)) v >>= 8, n |= I(8); \
if(v & I(0x00f0)) v >>= 4, n |= I(4); \
if(v & I(0x000c)) v >>= 2, n |= I(2); \
if(v & I(0x0002)) v >>= 1, n |= I(1); \
return n
#define _c4_msb32_fallback \
unsigned n = 0; \
if(v & I(0xffff0000)) v >>= 16, n |= 16; \
if(v & I(0x0000ff00)) v >>= 8, n |= 8; \
if(v & I(0x000000f0)) v >>= 4, n |= 4; \
if(v & I(0x0000000c)) v >>= 2, n |= 2; \
if(v & I(0x00000002)) v >>= 1, n |= 1; \
return n
#define _c4_msb64_fallback \
unsigned n = 0; \
if(v & I(0xffffffff00000000)) v >>= 32, n |= I(32); \
if(v & I(0x00000000ffff0000)) v >>= 16, n |= I(16); \
if(v & I(0x000000000000ff00)) v >>= 8, n |= I(8); \
if(v & I(0x00000000000000f0)) v >>= 4, n |= I(4); \
if(v & I(0x000000000000000c)) v >>= 2, n |= I(2); \
if(v & I(0x0000000000000002)) v >>= 1, n |= I(1); \
return n
// u8
template<class I>
C4_CONSTEXPR14
auto msb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 1u, unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_MSB_INTRINSIC(__builtin_clz)
// upcast to use the intrinsic, it's cheaper.
// Then remember that the upcast makes it to 31bits
#ifdef C4_MSVC
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanReverse(&bit, (unsigned long)v);
return bit;
#else
_c4_msb8_fallback;
#endif
#else
return 31u - (unsigned)__builtin_clz((unsigned)v);
#endif
#else
_c4_msb8_fallback;
#endif
}
// u16
template<class I>
C4_CONSTEXPR14
auto msb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 2u, unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_MSB_INTRINSIC(__builtin_clz)
// upcast to use the intrinsic, it's cheaper.
// Then remember that the upcast makes it to 31bits
#ifdef C4_MSVC
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanReverse(&bit, (unsigned long)v);
return bit;
#else
_c4_msb16_fallback;
#endif
#else
return 31u - (unsigned)__builtin_clz((unsigned)v);
#endif
#else
_c4_msb16_fallback;
#endif
}
// u32
template<class I>
C4_CONSTEXPR14
auto msb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 4u, unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_MSB_INTRINSIC(__builtin_clz)
#ifdef C4_MSVC
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanReverse(&bit, v);
return bit;
#else
_c4_msb32_fallback;
#endif
#else
return 31u - (unsigned)__builtin_clz((unsigned)v);
#endif
#else
_c4_msb32_fallback;
#endif
}
// u64 in 64bits
template<class I>
C4_CONSTEXPR14
auto msb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long) == 8u, unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_MSB_INTRINSIC(__builtin_clzl)
#ifdef C4_MSVC
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanReverse64(&bit, v);
return bit;
#else
_c4_msb64_fallback;
#endif
#else
return 63u - (unsigned)__builtin_clzl((unsigned long)v);
#endif
#else
_c4_msb64_fallback;
#endif
}
// u64 in 32bits
template<class I>
C4_CONSTEXPR14
auto msb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long long) == 8u && sizeof(unsigned long) != sizeof(unsigned long long), unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_MSB_INTRINSIC(__builtin_clzll)
#ifdef C4_MSVC
#if !defined(C4_CPU_X86) && !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanReverse64(&bit, v);
return bit;
#else
_c4_msb64_fallback;
#endif
#else
return 63u - (unsigned)__builtin_clzll((unsigned long long)v);
#endif
#else
_c4_msb64_fallback;
#endif
}
#undef _c4_msb8_fallback
#undef _c4_msb16_fallback
#undef _c4_msb32_fallback
#undef _c4_msb64_fallback
/** @} */
namespace detail {
template<class I, I val, I num_bits, bool finished> struct _msb11;
template<class I, I val, I num_bits>
struct _msb11< I, val, num_bits, false>
{
enum : unsigned { num = _msb11<I, (val>>1), num_bits+I(1), ((val>>1)==I(0))>::num };
};
template<class I, I val, I num_bits>
struct _msb11<I, val, num_bits, true>
{
static_assert(val == 0, "bad implementation");
enum : unsigned { num = (unsigned)(num_bits-1) };
};
} // namespace detail
/** TMP version of msb(); this needs to be implemented with template
* meta-programming because C++11 cannot use a constexpr function with
* local variables
* @see msb */
template<class I, I number>
struct msb11
{
enum : unsigned { value = detail::_msb11<I, number, 0, (number==I(0))>::num };
};
#undef _C4_USE_LSB_INTRINSIC
#undef _C4_USE_MSB_INTRINSIC
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// there is an implicit conversion below; it happens when E or B are
// narrower than int, and thus any operation will upcast the result to
// int, and then downcast to assign
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wconversion")
/** integer power; this function is constexpr-14 because of the local
* variables */
template<class B, class E>
C4_CONSTEXPR14 C4_CONST auto ipow(B base, E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type
{
C4_STATIC_ASSERT(std::is_integral<E>::value);
B r = B(1);
if(exponent >= 0)
{
for(E e = 0; e < exponent; ++e)
r *= base;
}
else
{
exponent *= E(-1);
for(E e = 0; e < exponent; ++e)
r /= base;
}
return r;
}
/** integer power; this function is constexpr-14 because of the local
* variables */
template<class B, B base, class E>
C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type
{
C4_STATIC_ASSERT(std::is_integral<E>::value);
B r = B(1);
if(exponent >= 0)
{
for(E e = 0; e < exponent; ++e)
r *= base;
}
else
{
exponent *= E(-1);
for(E e = 0; e < exponent; ++e)
r /= base;
}
return r;
}
/** integer power; this function is constexpr-14 because of the local
* variables */
template<class B, class Base, Base base, class E>
C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type
{
C4_STATIC_ASSERT(std::is_integral<E>::value);
B r = B(1);
B bbase = B(base);
if(exponent >= 0)
{
for(E e = 0; e < exponent; ++e)
r *= bbase;
}
else
{
exponent *= E(-1);
for(E e = 0; e < exponent; ++e)
r /= bbase;
}
return r;
}
/** integer power; this function is constexpr-14 because of the local
* variables */
template<class B, class E>
C4_CONSTEXPR14 C4_CONST auto ipow(B base, E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type
{
C4_STATIC_ASSERT(std::is_integral<E>::value);
B r = B(1);
for(E e = 0; e < exponent; ++e)
r *= base;
return r;
}
/** integer power; this function is constexpr-14 because of the local
* variables */
template<class B, B base, class E>
C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type
{
C4_STATIC_ASSERT(std::is_integral<E>::value);
B r = B(1);
for(E e = 0; e < exponent; ++e)
r *= base;
return r;
}
/** integer power; this function is constexpr-14 because of the local
* variables */
template<class B, class Base, Base base, class E>
C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type
{
C4_STATIC_ASSERT(std::is_integral<E>::value);
B r = B(1);
B bbase = B(base);
for(E e = 0; e < exponent; ++e)
r *= bbase;
return r;
}
C4_SUPPRESS_WARNING_GCC_CLANG_POP
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** return a mask with all bits set [first_bit,last_bit[; this function
* is constexpr-14 because of the local variables */
template<class I>
C4_CONSTEXPR14 I contiguous_mask(I first_bit, I last_bit)
{
I r = 0;
for(I i = first_bit; i < last_bit; ++i)
{
r |= (I(1) << i);
}
return r;
}
namespace detail {
template<class I, I val, I first, I last, bool finished>
struct _ctgmsk11;
template<class I, I val, I first, I last>
struct _ctgmsk11< I, val, first, last, true>
{
enum : I { value = _ctgmsk11<I, val|(I(1)<<first), first+I(1), last, (first+1!=last)>::value };
};
template<class I, I val, I first, I last>
struct _ctgmsk11< I, val, first, last, false>
{
enum : I { value = val };
};
} // namespace detail
/** TMP version of contiguous_mask(); this needs to be implemented with template
* meta-programming because C++11 cannot use a constexpr function with
* local variables
* @see contiguous_mask */
template<class I, I first_bit, I last_bit>
struct contiguous_mask11
{
enum : I { value = detail::_ctgmsk11<I, I(0), first_bit, last_bit, (first_bit!=last_bit)>::value };
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** use Empty Base Class Optimization to reduce the size of a pair of
* potentially empty types*/
namespace detail {
typedef enum {
tpc_same,
tpc_same_empty,
tpc_both_empty,
tpc_first_empty,
tpc_second_empty,
tpc_general
} TightPairCase_e;
template<class First, class Second>
constexpr TightPairCase_e tpc_which_case()
{
return std::is_same<First, Second>::value ?
std::is_empty<First>::value ?
tpc_same_empty
:
tpc_same
:
std::is_empty<First>::value && std::is_empty<Second>::value ?
tpc_both_empty
:
std::is_empty<First>::value ?
tpc_first_empty
:
std::is_empty<Second>::value ?
tpc_second_empty
:
tpc_general
;
}
template<class First, class Second, TightPairCase_e Case>
struct tight_pair
{
private:
First m_first;
Second m_second;
public:
using first_type = First;
using second_type = Second;
tight_pair() : m_first(), m_second() {}
tight_pair(First const& f, Second const& s) : m_first(f), m_second(s) {}
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return m_first; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return m_first; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; }
};
template<class First, class Second>
struct tight_pair<First, Second, tpc_same_empty> : public First
{
static_assert(std::is_same<First, Second>::value, "bad implementation");
using first_type = First;
using second_type = Second;
tight_pair() : First() {}
tight_pair(First const& f, Second const& /*s*/) : First(f) {}
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return reinterpret_cast<Second &>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return reinterpret_cast<Second const&>(*this); }
};
template<class First, class Second>
struct tight_pair<First, Second, tpc_both_empty> : public First, public Second
{
using first_type = First;
using second_type = Second;
tight_pair() : First(), Second() {}
tight_pair(First const& f, Second const& s) : First(f), Second(s) {}
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return static_cast<Second &>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return static_cast<Second const&>(*this); }
};
template<class First, class Second>
struct tight_pair<First, Second, tpc_same> : public First
{
Second m_second;
using first_type = First;
using second_type = Second;
tight_pair() : First() {}
tight_pair(First const& f, Second const& s) : First(f), m_second(s) {}
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; }
};
template<class First, class Second>
struct tight_pair<First, Second, tpc_first_empty> : public First
{
Second m_second;
using first_type = First;
using second_type = Second;
tight_pair() : First(), m_second() {}
tight_pair(First const& f, Second const& s) : First(f), m_second(s) {}
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; }
};
template<class First, class Second>
struct tight_pair<First, Second, tpc_second_empty> : public Second
{
First m_first;
using first_type = First;
using second_type = Second;
tight_pair() : Second(), m_first() {}
tight_pair(First const& f, Second const& s) : Second(s), m_first(f) {}
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return m_first; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return m_first; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return static_cast<Second &>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return static_cast<Second const&>(*this); }
};
} // namespace detail
template<class First, class Second>
using tight_pair = detail::tight_pair<First, Second, detail::tpc_which_case<First,Second>()>;
C4_SUPPRESS_WARNING_GCC_CLANG_POP
} // namespace c4
#endif /* _C4_MEMORY_UTIL_HPP_ */

View File

@ -0,0 +1,46 @@
#ifndef _C4_PLATFORM_HPP_
#define _C4_PLATFORM_HPP_
/** @file platform.hpp Provides platform information macros
* @ingroup basic_headers */
// see also https://sourceforge.net/p/predef/wiki/OperatingSystems/
#if defined(_WIN64)
# define C4_WIN
# define C4_WIN64
#elif defined(_WIN32)
# define C4_WIN
# define C4_WIN32
#elif defined(__ANDROID__)
# define C4_ANDROID
#elif defined(__APPLE__)
# include "TargetConditionals.h"
# if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
# define C4_IOS
# elif TARGET_OS_MAC || TARGET_OS_OSX
# define C4_MACOS
# else
# error "Unknown Apple platform"
# endif
#elif defined(__linux__) || defined(__linux)
# define C4_UNIX
# define C4_LINUX
#elif defined(__unix__) || defined(__unix)
# define C4_UNIX
#elif defined(__arm__) || defined(__aarch64__)
# define C4_ARM
#elif defined(__xtensa__) || defined(__XTENSA__)
# define C4_XTENSA
#elif defined(SWIG)
# define C4_SWIG
#else
# error "unknown platform"
#endif
#if defined(__posix) || defined(C4_UNIX) || defined(C4_LINUX)
# define C4_POSIX
#endif
#endif /* _C4_PLATFORM_HPP_ */

View File

@ -0,0 +1,123 @@
#ifndef _C4_PREPROCESSOR_HPP_
#define _C4_PREPROCESSOR_HPP_
/** @file preprocessor.hpp Contains basic macros and preprocessor utilities.
* @ingroup basic_headers */
#ifdef __clang__
/* NOTE: using , ## __VA_ARGS__ to deal with zero-args calls to
* variadic macros is not portable, but works in clang, gcc, msvc, icc.
* clang requires switching off compiler warnings for pedantic mode.
* @see http://stackoverflow.com/questions/32047685/variadic-macro-without-arguments */
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" // warning: token pasting of ',' and __VA_ARGS__ is a GNU extension
#elif defined(__GNUC__)
/* GCC also issues a warning for zero-args calls to variadic macros.
* This warning is switched on with -pedantic and apparently there is no
* easy way to turn it off as with clang. But marking this as a system
* header works.
* @see https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html
* @see http://stackoverflow.com/questions/35587137/ */
# pragma GCC system_header
#endif
#define C4_WIDEN(str) L"" str
#define C4_COUNTOF(arr) (sizeof(arr)/sizeof((arr)[0]))
#define C4_EXPAND(arg) arg
/** useful in some macro calls with template arguments */
#define C4_COMMA ,
/** useful in some macro calls with template arguments
* @see C4_COMMA */
#define C4_COMMA_X C4_COMMA
/** expand and quote */
#define C4_XQUOTE(arg) _C4_XQUOTE(arg)
#define _C4_XQUOTE(arg) C4_QUOTE(arg)
#define C4_QUOTE(arg) #arg
/** expand and concatenate */
#define C4_XCAT(arg1, arg2) _C4_XCAT(arg1, arg2)
#define _C4_XCAT(arg1, arg2) C4_CAT(arg1, arg2)
#define C4_CAT(arg1, arg2) arg1##arg2
#define C4_VERSION_CAT(major, minor, patch) ((major)*10000 + (minor)*100 + (patch))
/** A preprocessor foreach. Spectacular trick taken from:
* http://stackoverflow.com/a/1872506/5875572
* The first argument is for a macro receiving a single argument,
* which will be called with every subsequent argument. There is
* currently a limit of 32 arguments, and at least 1 must be provided.
*
Example:
@code{.cpp}
struct Example {
int a;
int b;
int c;
};
// define a one-arg macro to be called
#define PRN_STRUCT_OFFSETS(field) PRN_STRUCT_OFFSETS_(Example, field)
#define PRN_STRUCT_OFFSETS_(structure, field) printf(C4_XQUOTE(structure) ":" C4_XQUOTE(field)" - offset=%zu\n", offsetof(structure, field));
// now call the macro for a, b and c
C4_FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c);
@endcode */
#define C4_FOR_EACH(what, ...) C4_FOR_EACH_SEP(what, ;, __VA_ARGS__)
/** same as C4_FOR_EACH(), but use a custom separator between statements.
* If a comma is needed as the separator, use the C4_COMMA macro.
* @see C4_FOR_EACH
* @see C4_COMMA
*/
#define C4_FOR_EACH_SEP(what, sep, ...) _C4_FOR_EACH_(_C4_FOR_EACH_NARG(__VA_ARGS__), what, sep, __VA_ARGS__)
/// @cond dev
#define _C4_FOR_EACH_01(what, sep, x) what(x) sep
#define _C4_FOR_EACH_02(what, sep, x, ...) what(x) sep _C4_FOR_EACH_01(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_03(what, sep, x, ...) what(x) sep _C4_FOR_EACH_02(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_04(what, sep, x, ...) what(x) sep _C4_FOR_EACH_03(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_05(what, sep, x, ...) what(x) sep _C4_FOR_EACH_04(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_06(what, sep, x, ...) what(x) sep _C4_FOR_EACH_05(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_07(what, sep, x, ...) what(x) sep _C4_FOR_EACH_06(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_08(what, sep, x, ...) what(x) sep _C4_FOR_EACH_07(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_09(what, sep, x, ...) what(x) sep _C4_FOR_EACH_08(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_10(what, sep, x, ...) what(x) sep _C4_FOR_EACH_09(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_11(what, sep, x, ...) what(x) sep _C4_FOR_EACH_10(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_12(what, sep, x, ...) what(x) sep _C4_FOR_EACH_11(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_13(what, sep, x, ...) what(x) sep _C4_FOR_EACH_12(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_14(what, sep, x, ...) what(x) sep _C4_FOR_EACH_13(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_15(what, sep, x, ...) what(x) sep _C4_FOR_EACH_14(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_16(what, sep, x, ...) what(x) sep _C4_FOR_EACH_15(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_17(what, sep, x, ...) what(x) sep _C4_FOR_EACH_16(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_18(what, sep, x, ...) what(x) sep _C4_FOR_EACH_17(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_19(what, sep, x, ...) what(x) sep _C4_FOR_EACH_18(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_20(what, sep, x, ...) what(x) sep _C4_FOR_EACH_19(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_21(what, sep, x, ...) what(x) sep _C4_FOR_EACH_20(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_22(what, sep, x, ...) what(x) sep _C4_FOR_EACH_21(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_23(what, sep, x, ...) what(x) sep _C4_FOR_EACH_22(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_24(what, sep, x, ...) what(x) sep _C4_FOR_EACH_23(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_25(what, sep, x, ...) what(x) sep _C4_FOR_EACH_24(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_26(what, sep, x, ...) what(x) sep _C4_FOR_EACH_25(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_27(what, sep, x, ...) what(x) sep _C4_FOR_EACH_26(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_28(what, sep, x, ...) what(x) sep _C4_FOR_EACH_27(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_29(what, sep, x, ...) what(x) sep _C4_FOR_EACH_28(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_30(what, sep, x, ...) what(x) sep _C4_FOR_EACH_29(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_31(what, sep, x, ...) what(x) sep _C4_FOR_EACH_30(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_32(what, sep, x, ...) what(x) sep _C4_FOR_EACH_31(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_NARG(...) _C4_FOR_EACH_NARG_(__VA_ARGS__, _C4_FOR_EACH_RSEQ_N())
#define _C4_FOR_EACH_NARG_(...) _C4_FOR_EACH_ARG_N(__VA_ARGS__)
#define _C4_FOR_EACH_ARG_N(_01, _02, _03, _04, _05, _06, _07, _08, _09, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, N, ...) N
#define _C4_FOR_EACH_RSEQ_N() 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 09, 08, 07, 06, 05, 04, 03, 02, 01
#define _C4_FOR_EACH_(N, what, sep, ...) C4_XCAT(_C4_FOR_EACH_, N)(what, sep, __VA_ARGS__)
/// @endcond
#ifdef __clang__
# pragma clang diagnostic pop
#endif
#endif /* _C4_PREPROCESSOR_HPP_ */

View File

@ -0,0 +1,11 @@
#ifndef _C4_STD_STD_HPP_
#define _C4_STD_STD_HPP_
/** @file std.hpp includes all c4-std interop files */
#include "c4/std/vector.hpp"
#include "c4/std/string.hpp"
#include "c4/std/string_view.hpp"
#include "c4/std/tuple.hpp"
#endif // _C4_STD_STD_HPP_

View File

@ -0,0 +1,10 @@
#ifndef _C4_STD_STD_FWD_HPP_
#define _C4_STD_STD_FWD_HPP_
/** @file std_fwd.hpp includes all c4-std interop fwd files */
#include "c4/std/vector_fwd.hpp"
#include "c4/std/string_fwd.hpp"
//#include "c4/std/tuple_fwd.hpp"
#endif // _C4_STD_STD_FWD_HPP_

View File

@ -0,0 +1,97 @@
#ifndef _C4_STD_STRING_HPP_
#define _C4_STD_STRING_HPP_
/** @file string.hpp */
#ifndef C4CORE_SINGLE_HEADER
#include "c4/substr.hpp"
#endif
#include <string>
namespace c4 {
//-----------------------------------------------------------------------------
/** get a writeable view to an existing std::string.
* When the string is empty, the returned view will be pointing
* at the character with value '\0', but the size will be zero.
* @see https://en.cppreference.com/w/cpp/string/basic_string/operator_at
*/
C4_ALWAYS_INLINE c4::substr to_substr(std::string &s) noexcept
{
#if C4_CPP < 11
#error this function will have undefined behavior
#endif
// since c++11 it is legal to call s[s.size()].
return c4::substr(&s[0], s.size());
}
/** get a readonly view to an existing std::string.
* When the string is empty, the returned view will be pointing
* at the character with value '\0', but the size will be zero.
* @see https://en.cppreference.com/w/cpp/string/basic_string/operator_at
*/
C4_ALWAYS_INLINE c4::csubstr to_csubstr(std::string const& s) noexcept
{
#if C4_CPP < 11
#error this function will have undefined behavior
#endif
// since c++11 it is legal to call s[s.size()].
return c4::csubstr(&s[0], s.size());
}
//-----------------------------------------------------------------------------
C4_ALWAYS_INLINE bool operator== (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) == 0; }
C4_ALWAYS_INLINE bool operator!= (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) != 0; }
C4_ALWAYS_INLINE bool operator>= (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) >= 0; }
C4_ALWAYS_INLINE bool operator> (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) > 0; }
C4_ALWAYS_INLINE bool operator<= (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) <= 0; }
C4_ALWAYS_INLINE bool operator< (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) < 0; }
C4_ALWAYS_INLINE bool operator== (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) == 0; }
C4_ALWAYS_INLINE bool operator!= (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) != 0; }
C4_ALWAYS_INLINE bool operator>= (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) <= 0; }
C4_ALWAYS_INLINE bool operator> (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) < 0; }
C4_ALWAYS_INLINE bool operator<= (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) >= 0; }
C4_ALWAYS_INLINE bool operator< (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) > 0; }
//-----------------------------------------------------------------------------
/** copy an std::string to a writeable string view */
inline size_t to_chars(c4::substr buf, std::string const& s)
{
C4_ASSERT(!buf.overlaps(to_csubstr(s)));
size_t len = buf.len < s.size() ? buf.len : s.size();
// calling memcpy with null strings is undefined behavior
// and will wreak havoc in calling code's branches.
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
if(len)
{
C4_ASSERT(s.data() != nullptr);
C4_ASSERT(buf.str != nullptr);
memcpy(buf.str, s.data(), len);
}
return s.size(); // return the number of needed chars
}
/** copy a string view to an existing std::string */
inline bool from_chars(c4::csubstr buf, std::string * s)
{
s->resize(buf.len);
C4_ASSERT(!buf.overlaps(to_csubstr(*s)));
// calling memcpy with null strings is undefined behavior
// and will wreak havoc in calling code's branches.
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
if(buf.len)
{
C4_ASSERT(buf.str != nullptr);
memcpy(&(*s)[0], buf.str, buf.len);
}
return true;
}
} // namespace c4
#endif // _C4_STD_STRING_HPP_

View File

@ -0,0 +1,59 @@
#ifndef _C4_STD_STRING_FWD_HPP_
#define _C4_STD_STRING_FWD_HPP_
/** @file string_fwd.hpp */
#ifndef DOXYGEN
#ifndef C4CORE_SINGLE_HEADER
#include "c4/substr_fwd.hpp"
#endif
#include <cstddef>
// forward declarations for std::string
#if defined(__GLIBCXX__) || defined(__GLIBCPP__)
#include <bits/stringfwd.h> // use the fwd header in glibcxx
#elif defined(_LIBCPP_VERSION) || defined(__APPLE_CC__)
#include <iosfwd> // use the fwd header in stdlibc++
#elif defined(_MSC_VER)
#include "c4/error.hpp"
//! @todo is there a fwd header in msvc?
namespace std {
C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(4643) // Forward declaring 'char_traits' in namespace std is not permitted by the C++ Standard.
template<typename> struct char_traits;
template<typename> class allocator;
template<typename _CharT, typename _Traits, typename _Alloc> class basic_string;
using string = basic_string<char, char_traits<char>, allocator<char>>;
C4_SUPPRESS_WARNING_MSVC_POP
} /* namespace std */
#else
#error "unknown standard library"
#endif
namespace c4 {
c4::substr to_substr(std::string &s) noexcept;
c4::csubstr to_csubstr(std::string const& s) noexcept;
bool operator== (c4::csubstr ss, std::string const& s);
bool operator!= (c4::csubstr ss, std::string const& s);
bool operator>= (c4::csubstr ss, std::string const& s);
bool operator> (c4::csubstr ss, std::string const& s);
bool operator<= (c4::csubstr ss, std::string const& s);
bool operator< (c4::csubstr ss, std::string const& s);
bool operator== (std::string const& s, c4::csubstr ss);
bool operator!= (std::string const& s, c4::csubstr ss);
bool operator>= (std::string const& s, c4::csubstr ss);
bool operator> (std::string const& s, c4::csubstr ss);
bool operator<= (std::string const& s, c4::csubstr ss);
bool operator< (std::string const& s, c4::csubstr ss);
size_t to_chars(c4::substr buf, std::string const& s);
bool from_chars(c4::csubstr buf, std::string * s);
} // namespace c4
#endif // DOXYGEN
#endif // _C4_STD_STRING_FWD_HPP_

View File

@ -0,0 +1,71 @@
#ifndef _C4_STD_STRING_VIEW_HPP_
#define _C4_STD_STRING_VIEW_HPP_
/** @file string_view.hpp */
#ifndef C4CORE_SINGLE_HEADER
#include "c4/language.hpp"
#endif
#if (C4_CPP >= 17 && defined(__cpp_lib_string_view)) || defined(__DOXYGEN__)
#ifndef C4CORE_SINGLE_HEADER
#include "c4/substr.hpp"
#endif
#include <string_view>
namespace c4 {
//-----------------------------------------------------------------------------
/** create a csubstr from an existing std::string_view. */
C4_ALWAYS_INLINE c4::csubstr to_csubstr(std::string_view s) noexcept
{
return c4::csubstr(s.data(), s.size());
}
//-----------------------------------------------------------------------------
C4_ALWAYS_INLINE bool operator== (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) == 0; }
C4_ALWAYS_INLINE bool operator!= (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) != 0; }
C4_ALWAYS_INLINE bool operator>= (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) >= 0; }
C4_ALWAYS_INLINE bool operator> (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) > 0; }
C4_ALWAYS_INLINE bool operator<= (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) <= 0; }
C4_ALWAYS_INLINE bool operator< (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) < 0; }
C4_ALWAYS_INLINE bool operator== (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) == 0; }
C4_ALWAYS_INLINE bool operator!= (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) != 0; }
C4_ALWAYS_INLINE bool operator<= (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) >= 0; }
C4_ALWAYS_INLINE bool operator< (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) > 0; }
C4_ALWAYS_INLINE bool operator>= (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) <= 0; }
C4_ALWAYS_INLINE bool operator> (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) < 0; }
//-----------------------------------------------------------------------------
/** copy an std::string_view to a writeable substr */
inline size_t to_chars(c4::substr buf, std::string_view s)
{
C4_ASSERT(!buf.overlaps(to_csubstr(s)));
size_t sz = s.size();
size_t len = buf.len < sz ? buf.len : sz;
// calling memcpy with null strings is undefined behavior
// and will wreak havoc in calling code's branches.
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
if(len)
{
C4_ASSERT(s.data() != nullptr);
C4_ASSERT(buf.str != nullptr);
memcpy(buf.str, s.data(), len);
}
return sz; // return the number of needed chars
}
} // namespace c4
#endif // C4_STRING_VIEW_AVAILABLE
#endif // _C4_STD_STRING_VIEW_HPP_

View File

@ -0,0 +1,184 @@
#ifndef _C4_STD_TUPLE_HPP_
#define _C4_STD_TUPLE_HPP_
/** @file tuple.hpp */
#ifndef C4CORE_SINGLE_HEADER
#include "c4/format.hpp"
#endif
#include <tuple>
/** this is a work in progress */
#undef C4_TUPLE_TO_CHARS
namespace c4 {
#ifdef C4_TUPLE_TO_CHARS
namespace detail {
template< size_t Curr, class... Types >
struct tuple_helper
{
static size_t do_cat(substr buf, std::tuple< Types... > const& tp)
{
size_t num = to_chars(buf, std::get<Curr>(tp));
buf = buf.len >= num ? buf.sub(num) : substr{};
num += tuple_helper< Curr+1, Types... >::do_cat(buf, tp);
return num;
}
static size_t do_uncat(csubstr buf, std::tuple< Types... > & tp)
{
size_t num = from_str_trim(buf, &std::get<Curr>(tp));
if(num == csubstr::npos) return csubstr::npos;
buf = buf.len >= num ? buf.sub(num) : substr{};
num += tuple_helper< Curr+1, Types... >::do_uncat(buf, tp);
return num;
}
template< class Sep >
static size_t do_catsep_more(substr buf, Sep const& sep, std::tuple< Types... > const& tp)
{
size_t ret = to_chars(buf, sep), num = ret;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = to_chars(buf, std::get<Curr>(tp));
num += ret;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = tuple_helper< Curr+1, Types... >::do_catsep_more(buf, sep, tp);
num += ret;
return num;
}
template< class Sep >
static size_t do_uncatsep_more(csubstr buf, Sep & sep, std::tuple< Types... > & tp)
{
size_t ret = from_str_trim(buf, &sep), num = ret;
if(ret == csubstr::npos) return csubstr::npos;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = from_str_trim(buf, &std::get<Curr>(tp));
if(ret == csubstr::npos) return csubstr::npos;
num += ret;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = tuple_helper< Curr+1, Types... >::do_uncatsep_more(buf, sep, tp);
if(ret == csubstr::npos) return csubstr::npos;
num += ret;
return num;
}
static size_t do_format(substr buf, csubstr fmt, std::tuple< Types... > const& tp)
{
auto pos = fmt.find("{}");
if(pos != csubstr::npos)
{
size_t num = to_chars(buf, fmt.sub(0, pos));
size_t out = num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = to_chars(buf, std::get<Curr>(tp));
out += num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = tuple_helper< Curr+1, Types... >::do_format(buf, fmt.sub(pos + 2), tp);
out += num;
return out;
}
else
{
return format(buf, fmt);
}
}
static size_t do_unformat(csubstr buf, csubstr fmt, std::tuple< Types... > & tp)
{
auto pos = fmt.find("{}");
if(pos != csubstr::npos)
{
size_t num = pos;
size_t out = num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = from_str_trim(buf, &std::get<Curr>(tp));
out += num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = tuple_helper< Curr+1, Types... >::do_unformat(buf, fmt.sub(pos + 2), tp);
out += num;
return out;
}
else
{
return tuple_helper< sizeof...(Types), Types... >::do_unformat(buf, fmt, tp);
}
}
};
/** @todo VS compilation fails for this class */
template< class... Types >
struct tuple_helper< sizeof...(Types), Types... >
{
static size_t do_cat(substr /*buf*/, std::tuple<Types...> const& /*tp*/) { return 0; }
static size_t do_uncat(csubstr /*buf*/, std::tuple<Types...> & /*tp*/) { return 0; }
template< class Sep > static size_t do_catsep_more(substr /*buf*/, Sep const& /*sep*/, std::tuple<Types...> const& /*tp*/) { return 0; }
template< class Sep > static size_t do_uncatsep_more(csubstr /*buf*/, Sep & /*sep*/, std::tuple<Types...> & /*tp*/) { return 0; }
static size_t do_format(substr buf, csubstr fmt, std::tuple<Types...> const& /*tp*/)
{
return to_chars(buf, fmt);
}
static size_t do_unformat(csubstr buf, csubstr fmt, std::tuple<Types...> const& /*tp*/)
{
return 0;
}
};
} // namespace detail
template< class... Types >
inline size_t cat(substr buf, std::tuple< Types... > const& tp)
{
return detail::tuple_helper< 0, Types... >::do_cat(buf, tp);
}
template< class... Types >
inline size_t uncat(csubstr buf, std::tuple< Types... > & tp)
{
return detail::tuple_helper< 0, Types... >::do_uncat(buf, tp);
}
template< class Sep, class... Types >
inline size_t catsep(substr buf, Sep const& sep, std::tuple< Types... > const& tp)
{
size_t num = to_chars(buf, std::cref(std::get<0>(tp)));
buf = buf.len >= num ? buf.sub(num) : substr{};
num += detail::tuple_helper< 1, Types... >::do_catsep_more(buf, sep, tp);
return num;
}
template< class Sep, class... Types >
inline size_t uncatsep(csubstr buf, Sep & sep, std::tuple< Types... > & tp)
{
size_t ret = from_str_trim(buf, &std::get<0>(tp)), num = ret;
if(ret == csubstr::npos) return csubstr::npos;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = detail::tuple_helper< 1, Types... >::do_uncatsep_more(buf, sep, tp);
if(ret == csubstr::npos) return csubstr::npos;
num += ret;
return num;
}
template< class... Types >
inline size_t format(substr buf, csubstr fmt, std::tuple< Types... > const& tp)
{
return detail::tuple_helper< 0, Types... >::do_format(buf, fmt, tp);
}
template< class... Types >
inline size_t unformat(csubstr buf, csubstr fmt, std::tuple< Types... > & tp)
{
return detail::tuple_helper< 0, Types... >::do_unformat(buf, fmt, tp);
}
#endif // C4_TUPLE_TO_CHARS
} // namespace c4
#endif /* _C4_STD_TUPLE_HPP_ */

View File

@ -0,0 +1,88 @@
#ifndef _C4_STD_VECTOR_HPP_
#define _C4_STD_VECTOR_HPP_
/** @file vector.hpp provides conversion and comparison facilities
* from/between std::vector<char> to c4::substr and c4::csubstr.
* @todo add to_span() and friends
*/
#ifndef C4CORE_SINGLE_HEADER
#include "c4/substr.hpp"
#endif
#include <vector>
namespace c4 {
//-----------------------------------------------------------------------------
/** get a substr (writeable string view) of an existing std::vector<char> */
template<class Alloc>
c4::substr to_substr(std::vector<char, Alloc> &vec)
{
char *data = vec.empty() ? nullptr : vec.data(); // data() may or may not return a null pointer.
return c4::substr(data, vec.size());
}
/** get a csubstr (read-only string) view of an existing std::vector<char> */
template<class Alloc>
c4::csubstr to_csubstr(std::vector<char, Alloc> const& vec)
{
const char *data = vec.empty() ? nullptr : vec.data(); // data() may or may not return a null pointer.
return c4::csubstr(data, vec.size());
}
//-----------------------------------------------------------------------------
// comparisons between substrings and std::vector<char>
template<class Alloc> C4_ALWAYS_INLINE bool operator!= (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss != to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator== (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss == to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator>= (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss >= to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator> (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss > to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator<= (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss <= to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator< (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss < to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator!= (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss != to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator== (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss == to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator>= (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss <= to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator> (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss < to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator<= (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss >= to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator< (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss > to_csubstr(s); }
//-----------------------------------------------------------------------------
/** copy a std::vector<char> to a writeable string view */
template<class Alloc>
inline size_t to_chars(c4::substr buf, std::vector<char, Alloc> const& s)
{
C4_ASSERT(!buf.overlaps(to_csubstr(s)));
size_t len = buf.len < s.size() ? buf.len : s.size();
// calling memcpy with null strings is undefined behavior
// and will wreak havoc in calling code's branches.
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
if(len > 0)
{
memcpy(buf.str, s.data(), len);
}
return s.size(); // return the number of needed chars
}
/** copy a string view to an existing std::vector<char> */
template<class Alloc>
inline bool from_chars(c4::csubstr buf, std::vector<char, Alloc> * s)
{
s->resize(buf.len);
C4_ASSERT(!buf.overlaps(to_csubstr(*s)));
// calling memcpy with null strings is undefined behavior
// and will wreak havoc in calling code's branches.
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
if(buf.len > 0)
{
memcpy(&(*s)[0], buf.str, buf.len);
}
return true;
}
} // namespace c4
#endif // _C4_STD_VECTOR_HPP_

View File

@ -0,0 +1,66 @@
#ifndef _C4_STD_VECTOR_FWD_HPP_
#define _C4_STD_VECTOR_FWD_HPP_
/** @file vector_fwd.hpp */
#include <cstddef>
// forward declarations for std::vector
#if defined(__GLIBCXX__) || defined(__GLIBCPP__) || defined(_MSC_VER)
#if defined(_MSC_VER)
__pragma(warning(push))
__pragma(warning(disable : 4643))
#endif
namespace std {
template<typename> class allocator;
#ifdef _GLIBCXX_DEBUG
inline namespace __debug {
template<typename T, typename Alloc> class vector;
}
#else
template<typename T, typename Alloc> class vector;
#endif
} // namespace std
#if defined(_MSC_VER)
__pragma(warning(pop))
#endif
#elif defined(_LIBCPP_ABI_NAMESPACE)
namespace std {
inline namespace _LIBCPP_ABI_NAMESPACE {
template<typename> class allocator;
template<typename T, typename Alloc> class vector;
} // namespace _LIBCPP_ABI_NAMESPACE
} // namespace std
#else
#error "unknown standard library"
#endif
#ifndef C4CORE_SINGLE_HEADER
#include "c4/substr_fwd.hpp"
#endif
namespace c4 {
template<class Alloc> c4::substr to_substr(std::vector<char, Alloc> &vec);
template<class Alloc> c4::csubstr to_csubstr(std::vector<char, Alloc> const& vec);
template<class Alloc> bool operator!= (c4::csubstr ss, std::vector<char, Alloc> const& s);
template<class Alloc> bool operator== (c4::csubstr ss, std::vector<char, Alloc> const& s);
template<class Alloc> bool operator>= (c4::csubstr ss, std::vector<char, Alloc> const& s);
template<class Alloc> bool operator> (c4::csubstr ss, std::vector<char, Alloc> const& s);
template<class Alloc> bool operator<= (c4::csubstr ss, std::vector<char, Alloc> const& s);
template<class Alloc> bool operator< (c4::csubstr ss, std::vector<char, Alloc> const& s);
template<class Alloc> bool operator!= (std::vector<char, Alloc> const& s, c4::csubstr ss);
template<class Alloc> bool operator== (std::vector<char, Alloc> const& s, c4::csubstr ss);
template<class Alloc> bool operator>= (std::vector<char, Alloc> const& s, c4::csubstr ss);
template<class Alloc> bool operator> (std::vector<char, Alloc> const& s, c4::csubstr ss);
template<class Alloc> bool operator<= (std::vector<char, Alloc> const& s, c4::csubstr ss);
template<class Alloc> bool operator< (std::vector<char, Alloc> const& s, c4::csubstr ss);
template<class Alloc> size_t to_chars(c4::substr buf, std::vector<char, Alloc> const& s);
template<class Alloc> bool from_chars(c4::csubstr buf, std::vector<char, Alloc> * s);
} // namespace c4
#endif // _C4_STD_VECTOR_FWD_HPP_

2293
3rdparty/rapidyaml/include/c4/substr.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
#ifndef _C4_SUBSTR_FWD_HPP_
#define _C4_SUBSTR_FWD_HPP_
#include "c4/export.hpp"
namespace c4 {
#ifndef DOXYGEN
template<class C> struct basic_substring;
using csubstr = C4CORE_EXPORT basic_substring<const char>;
using substr = C4CORE_EXPORT basic_substring<char>;
#endif // !DOXYGEN
} // namespace c4
#endif /* _C4_SUBSTR_FWD_HPP_ */

View File

@ -0,0 +1,68 @@
#ifndef _C4_SZCONV_HPP_
#define _C4_SZCONV_HPP_
/** @file szconv.hpp utilities to deal safely with narrowing conversions */
#include "c4/config.hpp"
#include "c4/error.hpp"
#include <limits>
namespace c4 {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
/** @todo this would be so much easier with calls to numeric_limits::max()... */
template<class SizeOut, class SizeIn>
struct is_narrower_size : std::conditional
<
(std::is_signed<SizeOut>::value == std::is_signed<SizeIn>::value)
?
(sizeof(SizeOut) < sizeof(SizeIn))
:
(
(sizeof(SizeOut) < sizeof(SizeIn))
||
(
(sizeof(SizeOut) == sizeof(SizeIn))
&&
(std::is_signed<SizeOut>::value && std::is_unsigned<SizeIn>::value)
)
),
std::true_type,
std::false_type
>::type
{
static_assert(std::is_integral<SizeIn >::value, "must be integral type");
static_assert(std::is_integral<SizeOut>::value, "must be integral type");
};
/** when SizeOut is wider than SizeIn, assignment can occur without reservations */
template<class SizeOut, class SizeIn>
C4_ALWAYS_INLINE
typename std::enable_if< ! is_narrower_size<SizeOut, SizeIn>::value, SizeOut>::type
szconv(SizeIn sz) noexcept
{
return static_cast<SizeOut>(sz);
}
/** when SizeOut is narrower than SizeIn, narrowing will occur, so we check
* for overflow. Note that this check is done only if C4_XASSERT is enabled.
* @see C4_XASSERT */
template<class SizeOut, class SizeIn>
C4_ALWAYS_INLINE
typename std::enable_if<is_narrower_size<SizeOut, SizeIn>::value, SizeOut>::type
szconv(SizeIn sz) C4_NOEXCEPT_X
{
C4_XASSERT(sz >= 0);
C4_XASSERT_MSG((SizeIn)sz <= (SizeIn)std::numeric_limits<SizeOut>::max(), "size conversion overflow: in=%zu", (size_t)sz);
SizeOut szo = static_cast<SizeOut>(sz);
return szo;
}
C4_SUPPRESS_WARNING_GCC_CLANG_POP
} // namespace c4
#endif /* _C4_SZCONV_HPP_ */

503
3rdparty/rapidyaml/include/c4/types.hpp vendored Normal file
View File

@ -0,0 +1,503 @@
#ifndef _C4_TYPES_HPP_
#define _C4_TYPES_HPP_
#include <stdint.h>
#include <stddef.h>
#include <type_traits>
#if __cplusplus >= 201103L
#include <utility> // for integer_sequence and friends
#endif
#include "c4/preprocessor.hpp"
#include "c4/language.hpp"
/** @file types.hpp basic types, and utility macros and traits for types.
* @ingroup basic_headers */
/** @defgroup types Type utilities */
namespace c4 {
/** @defgroup intrinsic_types Intrinsic types
* @ingroup types
* @{ */
using cbyte = const char; /**< a constant byte */
using byte = char; /**< a mutable byte */
using i8 = int8_t;
using i16 = int16_t;
using i32 = int32_t;
using i64 = int64_t;
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
using f32 = float;
using f64 = double;
using ssize_t = typename std::make_signed<size_t>::type;
/** @} */
//--------------------------------------------------
/** @defgroup utility_types Utility types
* @ingroup types
* @{ */
// some tag types
#if !defined(__clang__) && defined(__GNUC__)
#pragma GCC diagnostic push
#if __GNUC__ >= 6
#pragma GCC diagnostic ignored "-Wunused-const-variable"
#endif
#endif
/** a tag type for initializing the containers with variadic arguments a la
* initializer_list, minus the initializer_list overload problems.
*/
struct aggregate_t {};
/** @see aggregate_t */
constexpr const aggregate_t aggregate{};
/** a tag type for specifying the initial capacity of allocatable contiguous storage */
struct with_capacity_t {};
/** @see with_capacity_t */
constexpr const with_capacity_t with_capacity{};
/** a tag type for disambiguating template parameter packs in variadic template overloads */
struct varargs_t {};
/** @see with_capacity_t */
constexpr const varargs_t varargs{};
#if !defined(__clang__) && defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
//--------------------------------------------------
/** whether a value should be used in place of a const-reference in argument passing. */
template<class T>
struct cref_uses_val
{
enum { value = (
std::is_scalar<T>::value
||
(
#if C4_CPP >= 20
(std::is_trivially_copyable<T>::value && std::is_standard_layout<T>::value)
#else
std::is_pod<T>::value
#endif
&&
sizeof(T) <= sizeof(size_t))) };
};
/** utility macro to override the default behaviour for c4::fastcref<T>
@see fastcref */
#define C4_CREF_USES_VAL(T) \
template<> \
struct cref_uses_val<T> \
{ \
enum { value = true }; \
};
/** Whether to use pass-by-value or pass-by-const-reference in a function argument
* or return type. */
template<class T>
using fastcref = typename std::conditional<c4::cref_uses_val<T>::value, T, T const&>::type;
//--------------------------------------------------
/** Just what its name says. Useful sometimes as a default empty policy class. */
struct EmptyStruct
{
template<class... T> EmptyStruct(T && ...){}
};
/** Just what its name says. Useful sometimes as a default policy class to
* be inherited from. */
struct EmptyStructVirtual
{
virtual ~EmptyStructVirtual() = default;
template<class... T> EmptyStructVirtual(T && ...){}
};
/** */
template<class T>
struct inheritfrom : public T {};
//--------------------------------------------------
// Utilities to make a class obey size restrictions (eg, min size or size multiple of).
// DirectX usually makes this restriction with uniform buffers.
// This is also useful for padding to prevent false-sharing.
/** how many bytes must be added to size such that the result is at least minsize? */
C4_ALWAYS_INLINE constexpr size_t min_remainder(size_t size, size_t minsize) noexcept
{
return size < minsize ? minsize-size : 0;
}
/** how many bytes must be added to size such that the result is a multiple of multipleof? */
C4_ALWAYS_INLINE constexpr size_t mult_remainder(size_t size, size_t multipleof) noexcept
{
return (((size % multipleof) != 0) ? (multipleof-(size % multipleof)) : 0);
}
/* force the following class to be tightly packed. */
#pragma pack(push, 1)
/** pad a class with more bytes at the end.
* @see http://stackoverflow.com/questions/21092415/force-c-structure-to-pack-tightly */
template<class T, size_t BytesToPadAtEnd>
struct Padded : public T
{
using T::T;
using T::operator=;
Padded(T const& val) : T(val) {}
Padded(T && val) : T(val) {}
char ___c4padspace___[BytesToPadAtEnd];
};
#pragma pack(pop)
/** When the padding argument is 0, we cannot declare the char[] array. */
template<class T>
struct Padded<T, 0> : public T
{
using T::T;
using T::operator=;
Padded(T const& val) : T(val) {}
Padded(T && val) : T(val) {}
};
/** make T have a size which is at least Min bytes */
template<class T, size_t Min>
using MinSized = Padded<T, min_remainder(sizeof(T), Min)>;
/** make T have a size which is a multiple of Mult bytes */
template<class T, size_t Mult>
using MultSized = Padded<T, mult_remainder(sizeof(T), Mult)>;
/** make T have a size which is simultaneously:
* -bigger or equal than Min
* -a multiple of Mult */
template<class T, size_t Min, size_t Mult>
using MinMultSized = MultSized<MinSized<T, Min>, Mult>;
/** make T be suitable for use as a uniform buffer. (at least with DirectX). */
template<class T>
using UbufSized = MinMultSized<T, 64, 16>;
//-----------------------------------------------------------------------------
#define C4_NO_COPY_CTOR(ty) ty(ty const&) = delete
#define C4_NO_MOVE_CTOR(ty) ty(ty &&) = delete
#define C4_NO_COPY_ASSIGN(ty) ty& operator=(ty const&) = delete
#define C4_NO_MOVE_ASSIGN(ty) ty& operator=(ty &&) = delete
#define C4_DEFAULT_COPY_CTOR(ty) ty(ty const&) noexcept = default
#define C4_DEFAULT_MOVE_CTOR(ty) ty(ty &&) noexcept = default
#define C4_DEFAULT_COPY_ASSIGN(ty) ty& operator=(ty const&) noexcept = default
#define C4_DEFAULT_MOVE_ASSIGN(ty) ty& operator=(ty &&) noexcept = default
#define C4_NO_COPY_OR_MOVE_CTOR(ty) \
C4_NO_COPY_CTOR(ty); \
C4_NO_MOVE_CTOR(ty)
#define C4_NO_COPY_OR_MOVE_ASSIGN(ty) \
C4_NO_COPY_ASSIGN(ty); \
C4_NO_MOVE_ASSIGN(ty)
#define C4_NO_COPY_OR_MOVE(ty) \
C4_NO_COPY_OR_MOVE_CTOR(ty); \
C4_NO_COPY_OR_MOVE_ASSIGN(ty)
#define C4_DEFAULT_COPY_AND_MOVE_CTOR(ty) \
C4_DEFAULT_COPY_CTOR(ty); \
C4_DEFAULT_MOVE_CTOR(ty)
#define C4_DEFAULT_COPY_AND_MOVE_ASSIGN(ty) \
C4_DEFAULT_COPY_ASSIGN(ty); \
C4_DEFAULT_MOVE_ASSIGN(ty)
#define C4_DEFAULT_COPY_AND_MOVE(ty) \
C4_DEFAULT_COPY_AND_MOVE_CTOR(ty); \
C4_DEFAULT_COPY_AND_MOVE_ASSIGN(ty)
/** @see https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable */
#define C4_MUST_BE_TRIVIAL_COPY(ty) \
static_assert(std::is_trivially_copyable<ty>::value, #ty " must be trivially copyable")
/** @} */
//-----------------------------------------------------------------------------
/** @defgroup traits_types Type traits utilities
* @ingroup types
* @{ */
// http://stackoverflow.com/questions/10821380/is-t-an-instance-of-a-template-in-c
template<template<typename...> class X, typename T> struct is_instance_of_tpl : std::false_type {};
template<template<typename...> class X, typename... Y> struct is_instance_of_tpl<X, X<Y...>> : std::true_type {};
//-----------------------------------------------------------------------------
/** SFINAE. use this macro to enable a template function overload
based on a compile-time condition.
@code
// define an overload for a non-pod type
template<class T, C4_REQUIRE_T(std::is_pod<T>::value)>
void foo() { std::cout << "pod type\n"; }
// define an overload for a non-pod type
template<class T, C4_REQUIRE_T(!std::is_pod<T>::value)>
void foo() { std::cout << "nonpod type\n"; }
struct non_pod
{
non_pod() : name("asdfkjhasdkjh") {}
const char *name;
};
int main()
{
foo<float>(); // prints "pod type"
foo<non_pod>(); // prints "nonpod type"
}
@endcode */
#define C4_REQUIRE_T(cond) typename std::enable_if<cond, bool>::type* = nullptr
/** enable_if for a return type
* @see C4_REQUIRE_T */
#define C4_REQUIRE_R(cond, type_) typename std::enable_if<cond, type_>::type
//-----------------------------------------------------------------------------
/** define a traits class reporting whether a type provides a member typedef */
#define C4_DEFINE_HAS_TYPEDEF(member_typedef) \
template<typename T> \
struct has_##stype \
{ \
private: \
\
typedef char yes; \
typedef struct { char array[2]; } no; \
\
template<typename C> \
static yes _test(typename C::member_typedef*); \
\
template<typename C> \
static no _test(...); \
\
public: \
\
enum { value = (sizeof(_test<T>(0)) == sizeof(yes)) }; \
\
}
/** @} */
//-----------------------------------------------------------------------------
/** @defgroup type_declarations Type declaration utilities
* @ingroup types
* @{ */
#define _c4_DEFINE_ARRAY_TYPES_WITHOUT_ITERATOR(T, I) \
\
using size_type = I; \
using ssize_type = typename std::make_signed<I>::type; \
using difference_type = typename std::make_signed<I>::type; \
\
using value_type = T; \
using pointer = T*; \
using const_pointer = T const*; \
using reference = T&; \
using const_reference = T const&
#define _c4_DEFINE_TUPLE_ARRAY_TYPES_WITHOUT_ITERATOR(interior_types, I) \
\
using size_type = I; \
using ssize_type = typename std::make_signed<I>::type; \
using difference_type = typename std::make_signed<I>::type; \
\
template<I n> using value_type = typename std::tuple_element< n, std::tuple<interior_types...>>::type; \
template<I n> using pointer = value_type<n>*; \
template<I n> using const_pointer = value_type<n> const*; \
template<I n> using reference = value_type<n>&; \
template<I n> using const_reference = value_type<n> const&
#define _c4_DEFINE_ARRAY_TYPES(T, I) \
\
_c4_DEFINE_ARRAY_TYPES_WITHOUT_ITERATOR(T, I); \
\
using iterator = T*; \
using const_iterator = T const*; \
using reverse_iterator = std::reverse_iterator<T*>; \
using const_reverse_iterator = std::reverse_iterator<T const*>
#define _c4_DEFINE_TUPLE_ARRAY_TYPES(interior_types, I) \
\
_c4_DEFINE_TUPLE_ARRAY_TYPES_WITHOUT_ITERATOR(interior_types, I); \
\
template<I n> using iterator = value_type<n>*; \
template<I n> using const_iterator = value_type<n> const*; \
template<I n> using reverse_iterator = std::reverse_iterator< value_type<n>*>; \
template<I n> using const_reverse_iterator = std::reverse_iterator< value_type<n> const*>
/** @} */
//-----------------------------------------------------------------------------
/** @defgroup compatility_utilities Backport implementation of some Modern C++ utilities
* @ingroup types
* @{ */
//-----------------------------------------------------------------------------
// index_sequence and friends are available only for C++14 and later.
// A C++11 implementation is provided here.
// This implementation was copied over from clang.
// see http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687
#if __cplusplus > 201103L
using std::integer_sequence;
using std::index_sequence;
using std::make_integer_sequence;
using std::make_index_sequence;
using std::index_sequence_for;
#else
/** C++11 implementation of integer sequence
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
template<class _Tp, _Tp... _Ip>
struct integer_sequence
{
static_assert(std::is_integral<_Tp>::value,
"std::integer_sequence can only be instantiated with an integral type" );
using value_type = _Tp;
static constexpr size_t size() noexcept { return sizeof...(_Ip); }
};
/** C++11 implementation of index sequence
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
template<size_t... _Ip>
using index_sequence = integer_sequence<size_t, _Ip...>;
/** @cond DONT_DOCUMENT_THIS */
namespace __detail {
template<typename _Tp, size_t ..._Extra>
struct __repeat;
template<typename _Tp, _Tp ..._Np, size_t ..._Extra>
struct __repeat<integer_sequence<_Tp, _Np...>, _Extra...>
{
using type = integer_sequence<_Tp,
_Np...,
sizeof...(_Np) + _Np...,
2 * sizeof...(_Np) + _Np...,
3 * sizeof...(_Np) + _Np...,
4 * sizeof...(_Np) + _Np...,
5 * sizeof...(_Np) + _Np...,
6 * sizeof...(_Np) + _Np...,
7 * sizeof...(_Np) + _Np...,
_Extra...>;
};
template<size_t _Np> struct __parity;
template<size_t _Np> struct __make : __parity<_Np % 8>::template __pmake<_Np> {};
template<> struct __make<0> { using type = integer_sequence<size_t>; };
template<> struct __make<1> { using type = integer_sequence<size_t, 0>; };
template<> struct __make<2> { using type = integer_sequence<size_t, 0, 1>; };
template<> struct __make<3> { using type = integer_sequence<size_t, 0, 1, 2>; };
template<> struct __make<4> { using type = integer_sequence<size_t, 0, 1, 2, 3>; };
template<> struct __make<5> { using type = integer_sequence<size_t, 0, 1, 2, 3, 4>; };
template<> struct __make<6> { using type = integer_sequence<size_t, 0, 1, 2, 3, 4, 5>; };
template<> struct __make<7> { using type = integer_sequence<size_t, 0, 1, 2, 3, 4, 5, 6>; };
template<> struct __parity<0> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type> {}; };
template<> struct __parity<1> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 1> {}; };
template<> struct __parity<2> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 2, _Np - 1> {}; };
template<> struct __parity<3> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 3, _Np - 2, _Np - 1> {}; };
template<> struct __parity<4> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
template<> struct __parity<5> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 5, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
template<> struct __parity<6> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 6, _Np - 5, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
template<> struct __parity<7> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 7, _Np - 6, _Np - 5, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
template<typename _Tp, typename _Up>
struct __convert
{
template<typename> struct __result;
template<_Tp ..._Np> struct __result<integer_sequence<_Tp, _Np...>>
{
using type = integer_sequence<_Up, _Np...>;
};
};
template<typename _Tp>
struct __convert<_Tp, _Tp>
{
template<typename _Up> struct __result
{
using type = _Up;
};
};
template<typename _Tp, _Tp _Np>
using __make_integer_sequence_unchecked = typename __detail::__convert<size_t, _Tp>::template __result<typename __detail::__make<_Np>::type>::type;
template<class _Tp, _Tp _Ep>
struct __make_integer_sequence
{
static_assert(std::is_integral<_Tp>::value,
"std::make_integer_sequence can only be instantiated with an integral type" );
static_assert(0 <= _Ep, "std::make_integer_sequence input shall not be negative");
typedef __make_integer_sequence_unchecked<_Tp, _Ep> type;
};
} // namespace __detail
/** @endcond */
/** C++11 implementation of index sequence
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
template<class _Tp, _Tp _Np>
using make_integer_sequence = typename __detail::__make_integer_sequence<_Tp, _Np>::type;
/** C++11 implementation of index sequence
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
template<size_t _Np>
using make_index_sequence = make_integer_sequence<size_t, _Np>;
/** C++11 implementation of index sequence
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
template<class... _Tp>
using index_sequence_for = make_index_sequence<sizeof...(_Tp)>;
#endif
/** @} */
} // namespace c4
#endif /* _C4_TYPES_HPP_ */

16
3rdparty/rapidyaml/include/c4/utf.hpp vendored Normal file
View File

@ -0,0 +1,16 @@
#ifndef C4_UTF_HPP_
#define C4_UTF_HPP_
#include "c4/language.hpp"
#include "c4/substr_fwd.hpp"
#include <stddef.h>
#include <stdint.h>
namespace c4 {
substr decode_code_point(substr out, csubstr code_point);
size_t decode_code_point(uint8_t *C4_RESTRICT buf, size_t buflen, const uint32_t code);
} // namespace c4
#endif // C4_UTF_HPP_

View File

@ -0,0 +1,10 @@
#ifndef _C4_WINDOWS_HPP_
#define _C4_WINDOWS_HPP_
#if defined(_WIN64) || defined(_WIN32)
#include "c4/windows_push.hpp"
#include <windows.h>
#include "c4/windows_pop.hpp"
#endif
#endif /* _C4_WINDOWS_HPP_ */

View File

@ -0,0 +1,41 @@
#ifndef _C4_WINDOWS_POP_HPP_
#define _C4_WINDOWS_POP_HPP_
#if defined(_WIN64) || defined(_WIN32)
#ifdef _c4_AMD64_
# undef _c4_AMD64_
# undef _AMD64_
#endif
#ifdef _c4_X86_
# undef _c4_X86_
# undef _X86_
#endif
#ifdef _c4_ARM_
# undef _c4_ARM_
# undef _ARM_
#endif
#ifdef _c4_NOMINMAX
# undef _c4_NOMINMAX
# undef NOMINMAX
#endif
#ifdef NOGDI
# undef _c4_NOGDI
# undef NOGDI
#endif
#ifdef VC_EXTRALEAN
# undef _c4_VC_EXTRALEAN
# undef VC_EXTRALEAN
#endif
#ifdef WIN32_LEAN_AND_MEAN
# undef _c4_WIN32_LEAN_AND_MEAN
# undef WIN32_LEAN_AND_MEAN
#endif
#endif /* defined(_WIN64) || defined(_WIN32) */
#endif /* _C4_WINDOWS_POP_HPP_ */

View File

@ -0,0 +1,102 @@
#ifndef _C4_WINDOWS_PUSH_HPP_
#define _C4_WINDOWS_PUSH_HPP_
/** @file windows_push.hpp sets up macros to include windows header files
* without pulling in all of <windows.h>
*
* @see #include windows_pop.hpp to undefine these macros
*
* @see https://aras-p.info/blog/2018/01/12/Minimizing-windows.h/ */
#if defined(_WIN64) || defined(_WIN32)
#if defined(_M_AMD64)
# ifndef _AMD64_
# define _c4_AMD64_
# define _AMD64_
# endif
#elif defined(_M_IX86)
# ifndef _X86_
# define _c4_X86_
# define _X86_
# endif
#elif defined(_M_ARM64)
# ifndef _ARM64_
# define _c4_ARM64_
# define _ARM64_
# endif
#elif defined(_M_ARM)
# ifndef _ARM_
# define _c4_ARM_
# define _ARM_
# endif
#endif
#ifndef NOMINMAX
# define _c4_NOMINMAX
# define NOMINMAX
#endif
#ifndef NOGDI
# define _c4_NOGDI
# define NOGDI
#endif
#ifndef VC_EXTRALEAN
# define _c4_VC_EXTRALEAN
# define VC_EXTRALEAN
#endif
#ifndef WIN32_LEAN_AND_MEAN
# define _c4_WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif
/* If defined, the following flags inhibit definition
* of the indicated items.
*
* NOGDICAPMASKS - CC_*, LC_*, PC_*, CP_*, TC_*, RC_
* NOVIRTUALKEYCODES - VK_*
* NOWINMESSAGES - WM_*, EM_*, LB_*, CB_*
* NOWINSTYLES - WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
* NOSYSMETRICS - SM_*
* NOMENUS - MF_*
* NOICONS - IDI_*
* NOKEYSTATES - MK_*
* NOSYSCOMMANDS - SC_*
* NORASTEROPS - Binary and Tertiary raster ops
* NOSHOWWINDOW - SW_*
* OEMRESOURCE - OEM Resource values
* NOATOM - Atom Manager routines
* NOCLIPBOARD - Clipboard routines
* NOCOLOR - Screen colors
* NOCTLMGR - Control and Dialog routines
* NODRAWTEXT - DrawText() and DT_*
* NOGDI - All GDI defines and routines
* NOKERNEL - All KERNEL defines and routines
* NOUSER - All USER defines and routines
* NONLS - All NLS defines and routines
* NOMB - MB_* and MessageBox()
* NOMEMMGR - GMEM_*, LMEM_*, GHND, LHND, associated routines
* NOMETAFILE - typedef METAFILEPICT
* NOMINMAX - Macros min(a,b) and max(a,b)
* NOMSG - typedef MSG and associated routines
* NOOPENFILE - OpenFile(), OemToAnsi, AnsiToOem, and OF_*
* NOSCROLL - SB_* and scrolling routines
* NOSERVICE - All Service Controller routines, SERVICE_ equates, etc.
* NOSOUND - Sound driver routines
* NOTEXTMETRIC - typedef TEXTMETRIC and associated routines
* NOWH - SetWindowsHook and WH_*
* NOWINOFFSETS - GWL_*, GCL_*, associated routines
* NOCOMM - COMM driver routines
* NOKANJI - Kanji support stuff.
* NOHELP - Help engine interface.
* NOPROFILER - Profiler interface.
* NODEFERWINDOWPOS - DeferWindowPos routines
* NOMCX - Modem Configuration Extensions
*/
#endif /* defined(_WIN64) || defined(_WIN32) */
#endif /* _C4_WINDOWS_PUSH_HPP_ */

View File

@ -0,0 +1,455 @@
#ifndef _C4_YML_COMMON_HPP_
#define _C4_YML_COMMON_HPP_
/** @file common.hpp Common utilities and infrastructure used by ryml. */
#include <cstddef>
#include <c4/substr.hpp>
#include <c4/yml/export.hpp>
//-----------------------------------------------------------------------------
// Specify groups to have a predefined topic order in doxygen:
/** @defgroup doc_quickstart Quickstart
*
* Example code for every feature.
*/
/** @defgroup doc_parse Parse utilities
* @see sample::sample_parse_in_place
* @see sample::sample_parse_in_arena
* @see sample::sample_parse_file
* @see sample::sample_parse_reuse_tree
* @see sample::sample_parse_reuse_parser
* @see sample::sample_parse_reuse_tree_and_parser
* @see sample::sample_location_tracking
*/
/** @defgroup doc_emit Emit utilities
*
* Utilities to emit YAML and JSON, either to a memory buffer or to a
* file or ostream-like class.
*
* @see sample::sample_emit_to_container
* @see sample::sample_emit_to_stream
* @see sample::sample_emit_to_file
* @see sample::sample_emit_nested_node
* @see sample::sample_emit_style
*/
/** @defgroup doc_node_type Node types
*/
/** @defgroup doc_tree Tree utilities
* @see sample::sample_quick_overview
* @see sample::sample_iterate_trees
* @see sample::sample_create_trees
* @see sample::sample_tree_arena
*
* @see sample::sample_static_trees
* @see sample::sample_location_tracking
*
* @see sample::sample_docs
* @see sample::sample_anchors_and_aliases
* @see sample::sample_tags
*/
/** @defgroup doc_node_classes Node classes
*
* High-level node classes.
*
* @see sample::sample_quick_overview
* @see sample::sample_iterate_trees
* @see sample::sample_create_trees
* @see sample::sample_tree_arena
*/
/** @defgroup doc_callbacks Callbacks for errors and allocation
*
* Functions called by ryml to allocate/free memory and to report
* errors.
*
* @see sample::sample_error_handler
* @see sample::sample_global_allocator
* @see sample::sample_per_tree_allocator
*/
/** @defgroup doc_serialization Serialization/deserialization
*
* Contains information on how to serialize and deserialize
* fundamental types, user scalar types, user container types and
* interop with std scalar/container types.
*
*/
/** @defgroup doc_tag_utils Tag utilities
* @see sample::sample_tags
*/
/** @defgroup doc_preprocessors Preprocessors
*
* Functions for preprocessing YAML prior to parsing.
*/
//-----------------------------------------------------------------------------
// document macros for doxygen
#ifdef __DOXYGEN__ // defined in Doxyfile::PREDEFINED
/** define this macro with a boolean value to enable/disable
* assertions to check preconditions and assumptions throughout the
* codebase; this causes a slowdown of the code, and larger code
* size. By default, this macro is defined unless NDEBUG is defined
* (see C4_USE_ASSERT); as a result, by default this macro is truthy
* only in debug builds. */
# define RYML_USE_ASSERT
/** (Undefined by default) Define this macro to disable ryml's default
* implementation of the callback functions; see @ref c4::yml::Callbacks */
# define RYML_NO_DEFAULT_CALLBACKS
/** (Undefined by default) When this macro is defined (and
* @ref RYML_NO_DEFAULT_CALLBACKS is not defined), the default error
* handler will throw C++ exceptions of type `std::runtime_error`. */
# define RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS
/** Conditionally expands to `noexcept` when @ref RYML_USE_ASSERT is 0 and
* is empty otherwise. The user is unable to override this macro. */
# define RYML_NOEXCEPT
#endif
//-----------------------------------------------------------------------------
/** @cond dev*/
#ifndef RYML_USE_ASSERT
# define RYML_USE_ASSERT C4_USE_ASSERT
#endif
#if RYML_USE_ASSERT
# define RYML_ASSERT(cond) RYML_CHECK(cond)
# define RYML_ASSERT_MSG(cond, msg) RYML_CHECK_MSG(cond, msg)
# define _RYML_CB_ASSERT(cb, cond) _RYML_CB_CHECK((cb), (cond))
# define RYML_NOEXCEPT
#else
# define RYML_ASSERT(cond)
# define RYML_ASSERT_MSG(cond, msg)
# define _RYML_CB_ASSERT(cb, cond)
# define RYML_NOEXCEPT noexcept
#endif
#define RYML_DEPRECATED(msg) C4_DEPRECATED(msg)
#define RYML_CHECK(cond) \
do { \
if(C4_UNLIKELY(!(cond))) \
{ \
RYML_DEBUG_BREAK() \
c4::yml::error("check failed: " #cond, c4::yml::Location(__FILE__, __LINE__, 0)); \
C4_UNREACHABLE_AFTER_ERR(); \
} \
} while(0)
#define RYML_CHECK_MSG(cond, msg) \
do \
{ \
if(C4_UNLIKELY(!(cond))) \
{ \
RYML_DEBUG_BREAK() \
c4::yml::error(msg ": check failed: " #cond, c4::yml::Location(__FILE__, __LINE__, 0)); \
C4_UNREACHABLE_AFTER_ERR(); \
} \
} while(0)
#if defined(RYML_DBG) && !defined(NDEBUG) && !defined(C4_NO_DEBUG_BREAK)
# define RYML_DEBUG_BREAK() \
{ \
if(c4::get_error_flags() & c4::ON_ERROR_DEBUGBREAK) \
{ \
C4_DEBUG_BREAK(); \
} \
}
#else
# define RYML_DEBUG_BREAK()
#endif
/** @endcond */
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
namespace c4 {
namespace yml {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
enum : size_t {
/** a null position */
npos = size_t(-1),
/** an index to none */
NONE = size_t(-1)
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//! holds a position into a source buffer
struct RYML_EXPORT LineCol
{
//! number of bytes from the beginning of the source buffer
size_t offset;
//! line
size_t line;
//! column
size_t col;
LineCol() : offset(), line(), col() {}
//! construct from line and column
LineCol(size_t l, size_t c) : offset(0), line(l), col(c) {}
//! construct from offset, line and column
LineCol(size_t o, size_t l, size_t c) : offset(o), line(l), col(c) {}
};
//! a source file position
struct RYML_EXPORT Location : public LineCol
{
csubstr name;
operator bool () const { return !name.empty() || line != 0 || offset != 0; }
Location() : LineCol(), name() {}
Location( size_t l, size_t c) : LineCol{ l, c}, name( ) {}
Location( csubstr n, size_t l, size_t c) : LineCol{ l, c}, name(n) {}
Location( csubstr n, size_t b, size_t l, size_t c) : LineCol{b, l, c}, name(n) {}
Location(const char *n, size_t l, size_t c) : LineCol{ l, c}, name(to_csubstr(n)) {}
Location(const char *n, size_t b, size_t l, size_t c) : LineCol{b, l, c}, name(to_csubstr(n)) {}
};
//-----------------------------------------------------------------------------
/** @addtogroup doc_callbacks
*
* @{ */
struct Callbacks;
/** set the global callbacks for the library; after a call to this
* function, these callbacks will be used by newly created objects
* (unless they are copying older objects with different
* callbacks). If @ref RYML_NO_DEFAULT_CALLBACKS is defined, it is
* mandatory to call this function prior to using any other library
* facility.
*
* @warning This function is NOT thread-safe.
*
* @warning the error callback must never return: see @ref pfn_error
* for more details */
RYML_EXPORT void set_callbacks(Callbacks const& c);
/** get the global callbacks
* @warning This function is not thread-safe. */
RYML_EXPORT Callbacks const& get_callbacks();
/** set the global callbacks back to their defaults ()
* @warning This function is not thread-safe. */
RYML_EXPORT void reset_callbacks();
/** the type of the function used to report errors
*
* @warning When given by the user, this function MUST interrupt
* execution, typically by either throwing an exception, or using
* `std::longjmp()` ([see
* documentation](https://en.cppreference.com/w/cpp/utility/program/setjmp))
* or by calling `std::abort()`. If the function returned, the parser
* would enter into an infinite loop, or the program may crash. */
using pfn_error = void (*) (const char* msg, size_t msg_len, Location location, void *user_data);
/** the type of the function used to allocate memory; ryml will only
* allocate memory through this callback. */
using pfn_allocate = void* (*)(size_t len, void* hint, void *user_data);
/** the type of the function used to free memory; ryml will only free
* memory through this callback. */
using pfn_free = void (*)(void* mem, size_t size, void *user_data);
/** a c-style callbacks class. Can be used globally by the library
* and/or locally by @ref Tree and @ref Parser objects. */
struct RYML_EXPORT Callbacks
{
void * m_user_data;
pfn_allocate m_allocate;
pfn_free m_free;
pfn_error m_error;
/** Construct an object with the default callbacks. If
* @ref RYML_NO_DEFAULT_CALLBACKS is defined, the object will have null
* members.*/
Callbacks();
/** Construct an object with the given callbacks.
*
* @param user_data Data to be forwarded in every call to a callback.
*
* @param alloc A pointer to an allocate function. Unless
* @ref RYML_NO_DEFAULT_CALLBACKS is defined, when this
* parameter is null, will fall back to ryml's default
* alloc implementation.
*
* @param free A pointer to a free function. Unless
* @ref RYML_NO_DEFAULT_CALLBACKS is defined, when this
* parameter is null, will fall back to ryml's default free
* implementation.
*
* @param error A pointer to an error function, which must never
* return (see @ref pfn_error). Unless
* @ref RYML_NO_DEFAULT_CALLBACKS is defined, when this
* parameter is null, will fall back to ryml's default
* error implementation.
*/
Callbacks(void *user_data, pfn_allocate alloc, pfn_free free, pfn_error error);
bool operator!= (Callbacks const& that) const { return !operator==(that); }
bool operator== (Callbacks const& that) const
{
return (m_user_data == that.m_user_data &&
m_allocate == that.m_allocate &&
m_free == that.m_free &&
m_error == that.m_error);
}
};
/** @} */
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
// BEWARE! MSVC requires that [[noreturn]] appears before RYML_EXPORT
[[noreturn]] RYML_EXPORT void error(Callbacks const& cb, const char *msg, size_t msg_len, Location loc);
[[noreturn]] RYML_EXPORT void error(const char *msg, size_t msg_len, Location loc);
[[noreturn]] inline void error(const char *msg, size_t msg_len)
{
error(msg, msg_len, Location{});
}
template<size_t N>
[[noreturn]] inline void error(const char (&msg)[N], Location loc)
{
error(msg, N-1, loc);
}
template<size_t N>
[[noreturn]] inline void error(const char (&msg)[N])
{
error(msg, N-1, Location{});
}
#define _RYML_CB_ERR(cb, msg_literal) \
do \
{ \
const char msg[] = msg_literal; \
RYML_DEBUG_BREAK() \
c4::yml::error((cb), \
msg, sizeof(msg), \
c4::yml::Location(__FILE__, 0, __LINE__, 0)); \
C4_UNREACHABLE_AFTER_ERR(); \
} while(0)
#define _RYML_CB_CHECK(cb, cond) \
do \
{ \
if(!(cond)) \
{ \
const char msg[] = "check failed: " #cond; \
RYML_DEBUG_BREAK() \
c4::yml::error((cb), \
msg, sizeof(msg), \
c4::yml::Location(__FILE__, 0, __LINE__, 0)); \
C4_UNREACHABLE_AFTER_ERR(); \
} \
} while(0)
#define _RYML_CB_ALLOC_HINT(cb, T, num, hint) (T*) (cb).m_allocate((num) * sizeof(T), (hint), (cb).m_user_data)
#define _RYML_CB_ALLOC(cb, T, num) _RYML_CB_ALLOC_HINT((cb), (T), (num), nullptr)
#define _RYML_CB_FREE(cb, buf, T, num) \
do { \
(cb).m_free((buf), (num) * sizeof(T), (cb).m_user_data); \
(buf) = nullptr; \
} while(0)
namespace detail {
template<int8_t signedval, uint8_t unsignedval>
struct _charconstant_t
: public std::conditional<std::is_signed<char>::value,
std::integral_constant<int8_t, signedval>,
std::integral_constant<uint8_t, unsignedval>>::type
{};
#define _RYML_CHCONST(signedval, unsignedval) ::c4::yml::detail::_charconstant_t<INT8_C(signedval), UINT8_C(unsignedval)>::value
} // namespace detail
namespace detail {
struct _SubstrWriter
{
substr buf;
size_t pos;
_SubstrWriter(substr buf_, size_t pos_=0) : buf(buf_), pos(pos_) {}
void append(csubstr s)
{
C4_ASSERT(!s.overlaps(buf));
if(s.len && pos + s.len <= buf.len)
{
C4_ASSERT(s.str);
memcpy(buf.str + pos, s.str, s.len);
}
pos += s.len;
}
void append(char c)
{
if(pos < buf.len)
buf.str[pos] = c;
++pos;
}
void append_n(char c, size_t numtimes)
{
if(numtimes && pos + numtimes < buf.len)
memset(buf.str + pos, c, numtimes);
pos += numtimes;
}
size_t slack() const { return pos <= buf.len ? buf.len - pos : 0; }
size_t excess() const { return pos > buf.len ? pos - buf.len : 0; }
//! get the part written so far
csubstr curr() const { return pos <= buf.len ? buf.first(pos) : buf; }
//! get the part that is still free to write to (the remainder)
substr rem() { return pos < buf.len ? buf.sub(pos) : buf.last(0); }
size_t advance(size_t more) { pos += more; return pos; }
};
} // namespace detail
/// @endcond
C4_SUPPRESS_WARNING_GCC_CLANG_POP
} // namespace yml
} // namespace c4
#endif /* _C4_YML_COMMON_HPP_ */

View File

@ -0,0 +1,200 @@
#ifndef C4_YML_DETAIL_CHECKS_HPP_
#define C4_YML_DETAIL_CHECKS_HPP_
#include "c4/yml/tree.hpp"
#ifdef __clang__
# pragma clang diagnostic push
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wtype-limits" // error: comparison of unsigned expression >= 0 is always true
#elif defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4296/*expression is always 'boolean_value'*/)
#endif
namespace c4 {
namespace yml {
void check_invariants(Tree const& t, size_t node=NONE);
void check_free_list(Tree const& t);
void check_arena(Tree const& t);
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void check_invariants(Tree const& t, size_t node)
{
if(node == NONE)
{
if(t.size() == 0) return;
node = t.root_id();
}
auto const& n = *t._p(node);
#ifdef RYML_DBG
if(n.m_first_child != NONE || n.m_last_child != NONE)
{
printf("check(%zu): fc=%zu lc=%zu\n", node, n.m_first_child, n.m_last_child);
}
else
{
printf("check(%zu)\n", node);
}
#endif
C4_CHECK(n.m_parent != node);
if(n.m_parent == NONE)
{
C4_CHECK(t.is_root(node));
}
else //if(n.m_parent != NONE)
{
C4_CHECK(t.has_child(n.m_parent, node));
auto const& p = *t._p(n.m_parent);
if(n.m_prev_sibling == NONE)
{
C4_CHECK(p.m_first_child == node);
C4_CHECK(t.first_sibling(node) == node);
}
else
{
C4_CHECK(p.m_first_child != node);
C4_CHECK(t.first_sibling(node) != node);
}
if(n.m_next_sibling == NONE)
{
C4_CHECK(p.m_last_child == node);
C4_CHECK(t.last_sibling(node) == node);
}
else
{
C4_CHECK(p.m_last_child != node);
C4_CHECK(t.last_sibling(node) != node);
}
}
C4_CHECK(n.m_first_child != node);
C4_CHECK(n.m_last_child != node);
if(n.m_first_child != NONE || n.m_last_child != NONE)
{
C4_CHECK(n.m_first_child != NONE);
C4_CHECK(n.m_last_child != NONE);
}
C4_CHECK(n.m_prev_sibling != node);
C4_CHECK(n.m_next_sibling != node);
if(n.m_prev_sibling != NONE)
{
C4_CHECK(t._p(n.m_prev_sibling)->m_next_sibling == node);
C4_CHECK(t._p(n.m_prev_sibling)->m_prev_sibling != node);
}
if(n.m_next_sibling != NONE)
{
C4_CHECK(t._p(n.m_next_sibling)->m_prev_sibling == node);
C4_CHECK(t._p(n.m_next_sibling)->m_next_sibling != node);
}
size_t count = 0;
for(size_t i = n.m_first_child; i != NONE; i = t.next_sibling(i))
{
#ifdef RYML_DBG
printf("check(%zu): descend to child[%zu]=%zu\n", node, count, i);
#endif
auto const& ch = *t._p(i);
C4_CHECK(ch.m_parent == node);
C4_CHECK(ch.m_next_sibling != i);
++count;
}
C4_CHECK(count == t.num_children(node));
if(n.m_prev_sibling == NONE && n.m_next_sibling == NONE)
{
if(n.m_parent != NONE)
{
C4_CHECK(t.num_children(n.m_parent) == 1);
C4_CHECK(t.num_siblings(node) == 1);
}
}
if(node == t.root_id())
{
C4_CHECK(t.size() == t.m_size);
C4_CHECK(t.capacity() == t.m_cap);
C4_CHECK(t.m_cap == t.m_size + t.slack());
check_free_list(t);
check_arena(t);
}
for(size_t i = t.first_child(node); i != NONE; i = t.next_sibling(i))
{
check_invariants(t, i);
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void check_free_list(Tree const& t)
{
if(t.m_free_head == NONE)
{
C4_CHECK(t.m_free_tail == t.m_free_head);
return;
}
C4_CHECK(t.m_free_head >= 0 && t.m_free_head < t.m_cap);
C4_CHECK(t.m_free_tail >= 0 && t.m_free_tail < t.m_cap);
auto const& head = *t._p(t.m_free_head);
//auto const& tail = *t._p(t.m_free_tail);
//C4_CHECK(head.m_prev_sibling == NONE);
//C4_CHECK(tail.m_next_sibling == NONE);
size_t count = 0;
for(size_t i = t.m_free_head, prev = NONE; i != NONE; i = t._p(i)->m_next_sibling)
{
auto const& elm = *t._p(i);
if(&elm != &head)
{
C4_CHECK(elm.m_prev_sibling == prev);
}
prev = i;
++count;
}
C4_CHECK(count == t.slack());
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void check_arena(Tree const& t)
{
C4_CHECK(t.m_arena.len == 0 || (t.m_arena_pos >= 0 && t.m_arena_pos <= t.m_arena.len));
C4_CHECK(t.arena_size() == t.m_arena_pos);
C4_CHECK(t.arena_slack() + t.m_arena_pos == t.m_arena.len);
}
} /* namespace yml */
} /* namespace c4 */
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#elif defined(_MSC_VER)
# pragma warning(pop)
#endif
#endif /* C4_YML_DETAIL_CHECKS_HPP_ */

View File

@ -0,0 +1,138 @@
#ifndef _C4_YML_DETAIL_PARSER_DBG_HPP_
#define _C4_YML_DETAIL_PARSER_DBG_HPP_
#ifndef _C4_YML_COMMON_HPP_
#include "../common.hpp"
#endif
#include <cstdio>
//-----------------------------------------------------------------------------
// some debugging scaffolds
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4068/*unknown pragma*/)
#endif
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
//#pragma GCC diagnostic ignored "-Wpragma-system-header-outside-header"
#pragma GCC system_header
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Werror"
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
// some debugging scaffolds
#ifdef RYML_DBG
#include <c4/dump.hpp>
namespace c4 {
inline void _dbg_dumper(csubstr s) { fwrite(s.str, 1, s.len, stdout); };
template<class ...Args>
void _dbg_printf(c4::csubstr fmt, Args&& ...args)
{
static char writebuf[256];
auto results = c4::format_dump_resume<&_dbg_dumper>(writebuf, fmt, std::forward<Args>(args)...);
// resume writing if the results failed to fit the buffer
if(C4_UNLIKELY(results.bufsize > sizeof(writebuf))) // bufsize will be that of the largest element serialized. Eg int(1), will require 1 byte.
{
results = format_dump_resume<&_dbg_dumper>(results, writebuf, fmt, std::forward<Args>(args)...);
if(C4_UNLIKELY(results.bufsize > sizeof(writebuf)))
{
results = format_dump_resume<&_dbg_dumper>(results, writebuf, fmt, std::forward<Args>(args)...);
}
}
}
} // namespace c4
# define _c4dbgt(fmt, ...) this->_dbg ("{}:{}: " fmt , __FILE__, __LINE__, ## __VA_ARGS__)
# define _c4dbgpf(fmt, ...) _dbg_printf("{}:{}: " fmt "\n", __FILE__, __LINE__, ## __VA_ARGS__)
# define _c4dbgp(msg) _dbg_printf("{}:{}: " msg "\n", __FILE__, __LINE__ )
# define _c4dbgq(msg) _dbg_printf(msg "\n")
# define _c4err(fmt, ...) \
do { if(c4::is_debugger_attached()) { C4_DEBUG_BREAK(); } \
this->_err("ERROR:\n" "{}:{}: " fmt, __FILE__, __LINE__, ## __VA_ARGS__); } while(0)
#else
# define _c4dbgt(fmt, ...)
# define _c4dbgpf(fmt, ...)
# define _c4dbgp(msg)
# define _c4dbgq(msg)
# define _c4err(fmt, ...) \
do { if(c4::is_debugger_attached()) { C4_DEBUG_BREAK(); } \
this->_err("ERROR: " fmt, ## __VA_ARGS__); } while(0)
#endif
#define _c4prsp(sp) sp
#define _c4presc(s) __c4presc(s.str, s.len)
inline c4::csubstr _c4prc(const char &C4_RESTRICT c)
{
switch(c)
{
case '\n': return c4::csubstr("\\n");
case '\t': return c4::csubstr("\\t");
case '\0': return c4::csubstr("\\0");
case '\r': return c4::csubstr("\\r");
case '\f': return c4::csubstr("\\f");
case '\b': return c4::csubstr("\\b");
case '\v': return c4::csubstr("\\v");
case '\a': return c4::csubstr("\\a");
default: return c4::csubstr(&c, 1);
}
}
inline void __c4presc(const char *s, size_t len)
{
size_t prev = 0;
for(size_t i = 0; i < len; ++i)
{
switch(s[i])
{
case '\n' : if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('n'); putchar('\n'); prev = i+1; break;
case '\t' : if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('t'); prev = i+1; break;
case '\0' : if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('0'); prev = i+1; break;
case '\r' : if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('r'); prev = i+1; break;
case '\f' : if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('f'); prev = i+1; break;
case '\b' : if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('b'); prev = i+1; break;
case '\v' : if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('v'); prev = i+1; break;
case '\a' : if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('a'); prev = i+1; break;
case '\x1b': if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('e'); prev = i+1; break;
case -0x3e/*0xc2u*/:
if(i+1 < len)
{
if(s[i+1] == -0x60/*0xa0u*/)
{
if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('_'); prev = i+2; ++i;
}
else if(s[i+1] == -0x7b/*0x85u*/)
{
if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('N'); prev = i+2; ++i;
}
break;
}
case -0x1e/*0xe2u*/:
if(i+2 < len && s[i+1] == -0x80/*0x80u*/)
{
if(s[i+2] == -0x58/*0xa8u*/)
{
if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('L'); prev = i+3; i += 2;
}
else if(s[i+2] == -0x57/*0xa9u*/)
{
if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('P'); prev = i+3; i += 2;
}
break;
}
}
}
if(len > prev)
fwrite(s + prev, 1, len - prev, stdout);
}
#pragma clang diagnostic pop
#pragma GCC diagnostic pop
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
#endif /* _C4_YML_DETAIL_PARSER_DBG_HPP_ */

View File

@ -0,0 +1,130 @@
#ifndef C4_YML_DETAIL_PRINT_HPP_
#define C4_YML_DETAIL_PRINT_HPP_
#include "c4/yml/tree.hpp"
#include "c4/yml/node.hpp"
namespace c4 {
namespace yml {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
inline size_t print_node(Tree const& p, size_t node, int level, size_t count, bool print_children)
{
printf("[%zd]%*s[%zd] %p", count, (2*level), "", node, (void const*)p.get(node));
if(p.is_root(node))
{
printf(" [ROOT]");
}
printf(" %s:", p.type_str(node));
if(p.has_key(node))
{
if(p.has_key_anchor(node))
{
csubstr ka = p.key_anchor(node);
printf(" &%.*s", (int)ka.len, ka.str);
}
if(p.has_key_tag(node))
{
csubstr kt = p.key_tag(node);
csubstr k = p.key(node);
printf(" %.*s '%.*s'", (int)kt.len, kt.str, (int)k.len, k.str);
}
else
{
csubstr k = p.key(node);
printf(" '%.*s'", (int)k.len, k.str);
}
}
else
{
RYML_ASSERT( ! p.has_key_tag(node));
}
if(p.has_val(node))
{
if(p.has_val_tag(node))
{
csubstr vt = p.val_tag(node);
csubstr v = p.val(node);
printf(" %.*s '%.*s'", (int)vt.len, vt.str, (int)v.len, v.str);
}
else
{
csubstr v = p.val(node);
printf(" '%.*s'", (int)v.len, v.str);
}
}
else
{
if(p.has_val_tag(node))
{
csubstr vt = p.val_tag(node);
printf(" %.*s", (int)vt.len, vt.str);
}
}
if(p.has_val_anchor(node))
{
auto &a = p.val_anchor(node);
printf(" valanchor='&%.*s'", (int)a.len, a.str);
}
printf(" (%zd sibs)", p.num_siblings(node));
++count;
if(p.is_container(node))
{
printf(" %zd children:\n", p.num_children(node));
if(print_children)
{
for(size_t i = p.first_child(node); i != NONE; i = p.next_sibling(i))
{
count = print_node(p, i, level+1, count, print_children);
}
}
}
else
{
printf("\n");
}
return count;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void print_node(ConstNodeRef const& p, int level=0)
{
print_node(*p.tree(), p.id(), level, 0, true);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline size_t print_tree(Tree const& p, size_t node=NONE)
{
printf("--------------------------------------\n");
size_t ret = 0;
if(!p.empty())
{
if(node == NONE)
node = p.root_id();
ret = print_node(p, node, 0, 0, true);
}
printf("#nodes=%zd vs #printed=%zd\n", p.size(), ret);
printf("--------------------------------------\n");
return ret;
}
C4_SUPPRESS_WARNING_GCC_CLANG_POP
} /* namespace yml */
} /* namespace c4 */
#endif /* C4_YML_DETAIL_PRINT_HPP_ */

View File

@ -0,0 +1,280 @@
#ifndef _C4_YML_DETAIL_STACK_HPP_
#define _C4_YML_DETAIL_STACK_HPP_
#ifndef _C4_YML_COMMON_HPP_
#include "../common.hpp"
#endif
#ifdef RYML_DBG
# include <type_traits>
#endif
#include <string.h>
namespace c4 {
namespace yml {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
namespace detail {
/** A lightweight contiguous stack with SSO. This avoids a dependency on std. */
template<class T, size_t N=16>
class stack
{
static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable");
static_assert(std::is_trivially_destructible<T>::value, "T must be trivially destructible");
enum : size_t { sso_size = N };
public:
T m_buf[N];
T * m_stack;
size_t m_size;
size_t m_capacity;
Callbacks m_callbacks;
public:
constexpr static bool is_contiguous() { return true; }
stack(Callbacks const& cb)
: m_buf()
, m_stack(m_buf)
, m_size(0)
, m_capacity(N)
, m_callbacks(cb) {}
stack() : stack(get_callbacks()) {}
~stack()
{
_free();
}
stack(stack const& that) RYML_NOEXCEPT : stack(that.m_callbacks)
{
resize(that.m_size);
_cp(&that);
}
stack(stack &&that) noexcept : stack(that.m_callbacks)
{
_mv(&that);
}
stack& operator= (stack const& that) RYML_NOEXCEPT
{
_cb(that.m_callbacks);
resize(that.m_size);
_cp(&that);
return *this;
}
stack& operator= (stack &&that) noexcept
{
_cb(that.m_callbacks);
_mv(&that);
return *this;
}
public:
size_t size() const { return m_size; }
size_t empty() const { return m_size == 0; }
size_t capacity() const { return m_capacity; }
void clear()
{
m_size = 0;
}
void resize(size_t sz)
{
reserve(sz);
m_size = sz;
}
void reserve(size_t sz);
void push(T const& C4_RESTRICT n)
{
RYML_ASSERT((const char*)&n + sizeof(T) < (const char*)m_stack || &n > m_stack + m_capacity);
if(m_size == m_capacity)
{
size_t cap = m_capacity == 0 ? N : 2 * m_capacity;
reserve(cap);
}
m_stack[m_size] = n;
++m_size;
}
void push_top()
{
RYML_ASSERT(m_size > 0);
if(m_size == m_capacity)
{
size_t cap = m_capacity == 0 ? N : 2 * m_capacity;
reserve(cap);
}
m_stack[m_size] = m_stack[m_size - 1];
++m_size;
}
T const& C4_RESTRICT pop()
{
RYML_ASSERT(m_size > 0);
--m_size;
return m_stack[m_size];
}
C4_ALWAYS_INLINE T const& C4_RESTRICT top() const { RYML_ASSERT(m_size > 0); return m_stack[m_size - 1]; }
C4_ALWAYS_INLINE T & C4_RESTRICT top() { RYML_ASSERT(m_size > 0); return m_stack[m_size - 1]; }
C4_ALWAYS_INLINE T const& C4_RESTRICT bottom() const { RYML_ASSERT(m_size > 0); return m_stack[0]; }
C4_ALWAYS_INLINE T & C4_RESTRICT bottom() { RYML_ASSERT(m_size > 0); return m_stack[0]; }
C4_ALWAYS_INLINE T const& C4_RESTRICT top(size_t i) const { RYML_ASSERT(i < m_size); return m_stack[m_size - 1 - i]; }
C4_ALWAYS_INLINE T & C4_RESTRICT top(size_t i) { RYML_ASSERT(i < m_size); return m_stack[m_size - 1 - i]; }
C4_ALWAYS_INLINE T const& C4_RESTRICT bottom(size_t i) const { RYML_ASSERT(i < m_size); return m_stack[i]; }
C4_ALWAYS_INLINE T & C4_RESTRICT bottom(size_t i) { RYML_ASSERT(i < m_size); return m_stack[i]; }
C4_ALWAYS_INLINE T const& C4_RESTRICT operator[](size_t i) const { RYML_ASSERT(i < m_size); return m_stack[i]; }
C4_ALWAYS_INLINE T & C4_RESTRICT operator[](size_t i) { RYML_ASSERT(i < m_size); return m_stack[i]; }
public:
using iterator = T *;
using const_iterator = T const *;
iterator begin() { return m_stack; }
iterator end () { return m_stack + m_size; }
const_iterator begin() const { return (const_iterator)m_stack; }
const_iterator end () const { return (const_iterator)m_stack + m_size; }
public:
void _free();
void _cp(stack const* C4_RESTRICT that);
void _mv(stack * that);
void _cb(Callbacks const& cb);
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template<class T, size_t N>
void stack<T, N>::reserve(size_t sz)
{
if(sz <= m_size)
return;
if(sz <= N)
{
m_stack = m_buf;
m_capacity = N;
return;
}
T *buf = (T*) m_callbacks.m_allocate(sz * sizeof(T), m_stack, m_callbacks.m_user_data);
memcpy(buf, m_stack, m_size * sizeof(T));
if(m_stack != m_buf)
{
m_callbacks.m_free(m_stack, m_capacity * sizeof(T), m_callbacks.m_user_data);
}
m_stack = buf;
m_capacity = sz;
}
//-----------------------------------------------------------------------------
template<class T, size_t N>
void stack<T, N>::_free()
{
RYML_ASSERT(m_stack != nullptr); // this structure cannot be memset() to zero
if(m_stack != m_buf)
{
m_callbacks.m_free(m_stack, m_capacity * sizeof(T), m_callbacks.m_user_data);
m_stack = m_buf;
m_size = N;
m_capacity = N;
}
else
{
RYML_ASSERT(m_capacity == N);
}
}
//-----------------------------------------------------------------------------
template<class T, size_t N>
void stack<T, N>::_cp(stack const* C4_RESTRICT that)
{
if(that->m_stack != that->m_buf)
{
RYML_ASSERT(that->m_capacity > N);
RYML_ASSERT(that->m_size <= that->m_capacity);
}
else
{
RYML_ASSERT(that->m_capacity <= N);
RYML_ASSERT(that->m_size <= that->m_capacity);
}
memcpy(m_stack, that->m_stack, that->m_size * sizeof(T));
m_size = that->m_size;
m_capacity = that->m_size < N ? N : that->m_size;
m_callbacks = that->m_callbacks;
}
//-----------------------------------------------------------------------------
template<class T, size_t N>
void stack<T, N>::_mv(stack * that)
{
if(that->m_stack != that->m_buf)
{
RYML_ASSERT(that->m_capacity > N);
RYML_ASSERT(that->m_size <= that->m_capacity);
m_stack = that->m_stack;
}
else
{
RYML_ASSERT(that->m_capacity <= N);
RYML_ASSERT(that->m_size <= that->m_capacity);
memcpy(m_buf, that->m_buf, that->m_size * sizeof(T));
m_stack = m_buf;
}
m_size = that->m_size;
m_capacity = that->m_capacity;
m_callbacks = that->m_callbacks;
// make sure no deallocation happens on destruction
RYML_ASSERT(that->m_stack != m_buf);
that->m_stack = that->m_buf;
that->m_capacity = N;
that->m_size = 0;
}
//-----------------------------------------------------------------------------
template<class T, size_t N>
void stack<T, N>::_cb(Callbacks const& cb)
{
if(cb != m_callbacks)
{
_free();
m_callbacks = cb;
}
}
} // namespace detail
C4_SUPPRESS_WARNING_GCC_CLANG_POP
} // namespace yml
} // namespace c4
#endif /* _C4_YML_DETAIL_STACK_HPP_ */

View File

@ -0,0 +1,970 @@
#ifndef _C4_YML_EMIT_DEF_HPP_
#define _C4_YML_EMIT_DEF_HPP_
#ifndef _C4_YML_EMIT_HPP_
#include "c4/yml/emit.hpp"
#endif
/** @file emit.def.hpp Definitions for emit functions. */
namespace c4 {
namespace yml {
template<class Writer>
substr Emitter<Writer>::emit_as(EmitType_e type, Tree const& t, size_t id, bool error_on_excess)
{
if(t.empty())
{
_RYML_CB_ASSERT(t.callbacks(), id == NONE);
return {};
}
if(id == NONE)
id = t.root_id();
_RYML_CB_CHECK(t.callbacks(), id < t.capacity());
m_tree = &t;
if(type == EMIT_YAML)
_emit_yaml(id);
else if(type == EMIT_JSON)
_do_visit_json(id);
else
_RYML_CB_ERR(m_tree->callbacks(), "unknown emit type");
m_tree = nullptr;
return this->Writer::_get(error_on_excess);
}
template<class Writer>
substr Emitter<Writer>::emit_as(EmitType_e type, Tree const& t, bool error_on_excess)
{
if(t.empty())
return {};
return this->emit_as(type, t, t.root_id(), error_on_excess);
}
template<class Writer>
substr Emitter<Writer>::emit_as(EmitType_e type, ConstNodeRef const& n, bool error_on_excess)
{
_RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
return this->emit_as(type, *n.tree(), n.id(), error_on_excess);
}
//-----------------------------------------------------------------------------
template<class Writer>
void Emitter<Writer>::_emit_yaml(size_t id)
{
// save branches in the visitor by doing the initial stream/doc
// logic here, sparing the need to check stream/val/keyval inside
// the visitor functions
auto dispatch = [this](size_t node){
NodeType ty = m_tree->type(node);
if(ty.marked_flow_sl())
_do_visit_flow_sl(node, 0);
else if(ty.marked_flow_ml())
_do_visit_flow_ml(node, 0);
else
{
_do_visit_block(node, 0);
}
};
if(!m_tree->is_root(id))
{
if(m_tree->is_container(id) && !m_tree->type(id).marked_flow())
{
size_t ilevel = 0;
if(m_tree->has_key(id))
{
this->Writer::_do_write(m_tree->key(id));
this->Writer::_do_write(":\n");
++ilevel;
}
_do_visit_block_container(id, ilevel, ilevel);
return;
}
}
auto *btd = m_tree->tag_directives().b;
auto *etd = m_tree->tag_directives().e;
auto write_tag_directives = [&btd, etd, this](size_t next_node){
auto end = btd;
while(end < etd)
{
if(end->next_node_id > next_node)
break;
++end;
}
for( ; btd != end; ++btd)
{
if(next_node != m_tree->first_child(m_tree->parent(next_node)))
this->Writer::_do_write("...\n");
this->Writer::_do_write("%TAG ");
this->Writer::_do_write(btd->handle);
this->Writer::_do_write(' ');
this->Writer::_do_write(btd->prefix);
this->Writer::_do_write('\n');
}
};
if(m_tree->is_stream(id))
{
if(m_tree->first_child(id) != NONE)
write_tag_directives(m_tree->first_child(id));
for(size_t child = m_tree->first_child(id); child != NONE; child = m_tree->next_sibling(child))
{
dispatch(child);
if(m_tree->next_sibling(child) != NONE)
write_tag_directives(m_tree->next_sibling(child));
}
}
else if(m_tree->is_container(id))
{
dispatch(id);
}
else if(m_tree->is_doc(id))
{
_RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->is_container(id)); // checked above
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_val(id)); // so it must be a val
_write_doc(id);
}
else if(m_tree->is_keyval(id))
{
_writek(id, 0);
this->Writer::_do_write(": ");
_writev(id, 0);
if(!m_tree->type(id).marked_flow())
this->Writer::_do_write('\n');
}
else if(m_tree->is_val(id))
{
//this->Writer::_do_write("- ");
_writev(id, 0);
if(!m_tree->type(id).marked_flow())
this->Writer::_do_write('\n');
}
else if(m_tree->type(id) == NOTYPE)
{
;
}
else
{
_RYML_CB_ERR(m_tree->callbacks(), "unknown type");
}
}
template<class Writer>
void Emitter<Writer>::_write_doc(size_t id)
{
RYML_ASSERT(m_tree->is_doc(id));
if(!m_tree->is_root(id))
{
RYML_ASSERT(m_tree->is_stream(m_tree->parent(id)));
this->Writer::_do_write("---");
}
if(!m_tree->has_val(id)) // this is more frequent
{
if(m_tree->has_val_tag(id))
{
if(!m_tree->is_root(id))
this->Writer::_do_write(' ');
_write_tag(m_tree->val_tag(id));
}
if(m_tree->has_val_anchor(id))
{
if(!m_tree->is_root(id))
this->Writer::_do_write(' ');
this->Writer::_do_write('&');
this->Writer::_do_write(m_tree->val_anchor(id));
}
}
else // docval
{
RYML_ASSERT(m_tree->has_val(id));
RYML_ASSERT(!m_tree->has_key(id));
if(!m_tree->is_root(id))
this->Writer::_do_write(' ');
_writev(id, 0);
}
this->Writer::_do_write('\n');
}
template<class Writer>
void Emitter<Writer>::_do_visit_flow_sl(size_t node, size_t ilevel)
{
RYML_ASSERT(!m_tree->is_stream(node));
RYML_ASSERT(m_tree->is_container(node) || m_tree->is_doc(node));
RYML_ASSERT(m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)));
if(m_tree->is_doc(node))
{
_write_doc(node);
if(!m_tree->has_children(node))
return;
}
else if(m_tree->is_container(node))
{
RYML_ASSERT(m_tree->is_map(node) || m_tree->is_seq(node));
bool spc = false; // write a space
if(m_tree->has_key(node))
{
_writek(node, ilevel);
this->Writer::_do_write(':');
spc = true;
}
if(m_tree->has_val_tag(node))
{
if(spc)
this->Writer::_do_write(' ');
_write_tag(m_tree->val_tag(node));
spc = true;
}
if(m_tree->has_val_anchor(node))
{
if(spc)
this->Writer::_do_write(' ');
this->Writer::_do_write('&');
this->Writer::_do_write(m_tree->val_anchor(node));
spc = true;
}
if(spc)
this->Writer::_do_write(' ');
if(m_tree->is_map(node))
{
this->Writer::_do_write('{');
}
else
{
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_seq(node));
this->Writer::_do_write('[');
}
} // container
for(size_t child = m_tree->first_child(node), count = 0; child != NONE; child = m_tree->next_sibling(child))
{
if(count++)
this->Writer::_do_write(',');
if(m_tree->is_keyval(child))
{
_writek(child, ilevel);
this->Writer::_do_write(": ");
_writev(child, ilevel);
}
else if(m_tree->is_val(child))
{
_writev(child, ilevel);
}
else
{
// with single-line flow, we can never go back to block
_do_visit_flow_sl(child, ilevel + 1);
}
}
if(m_tree->is_map(node))
{
this->Writer::_do_write('}');
}
else if(m_tree->is_seq(node))
{
this->Writer::_do_write(']');
}
}
C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(4702) // unreachable error, triggered by flow_ml not implemented
template<class Writer>
void Emitter<Writer>::_do_visit_flow_ml(size_t id, size_t ilevel, size_t do_indent)
{
C4_UNUSED(id);
C4_UNUSED(ilevel);
C4_UNUSED(do_indent);
c4::yml::error("not implemented");
}
template<class Writer>
void Emitter<Writer>::_do_visit_block_container(size_t node, size_t next_level, size_t do_indent)
{
RepC ind = indent_to(do_indent * next_level);
if(m_tree->is_seq(node))
{
for(size_t child = m_tree->first_child(node); child != NONE; child = m_tree->next_sibling(child))
{
_RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->has_key(child));
if(m_tree->is_val(child))
{
this->Writer::_do_write(ind);
this->Writer::_do_write("- ");
_writev(child, next_level);
this->Writer::_do_write('\n');
}
else
{
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_container(child));
NodeType ty = m_tree->type(child);
if(ty.marked_flow_sl())
{
this->Writer::_do_write(ind);
this->Writer::_do_write("- ");
_do_visit_flow_sl(child, 0u);
this->Writer::_do_write('\n');
}
else if(ty.marked_flow_ml())
{
this->Writer::_do_write(ind);
this->Writer::_do_write("- ");
_do_visit_flow_ml(child, next_level, do_indent);
this->Writer::_do_write('\n');
}
else
{
_do_visit_block(child, next_level, do_indent);
}
}
do_indent = true;
ind = indent_to(do_indent * next_level);
}
}
else // map
{
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_map(node));
for(size_t ich = m_tree->first_child(node); ich != NONE; ich = m_tree->next_sibling(ich))
{
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->has_key(ich));
if(m_tree->is_keyval(ich))
{
this->Writer::_do_write(ind);
_writek(ich, next_level);
this->Writer::_do_write(": ");
_writev(ich, next_level);
this->Writer::_do_write('\n');
}
else
{
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_container(ich));
NodeType ty = m_tree->type(ich);
if(ty.marked_flow_sl())
{
this->Writer::_do_write(ind);
_do_visit_flow_sl(ich, 0u);
this->Writer::_do_write('\n');
}
else if(ty.marked_flow_ml())
{
this->Writer::_do_write(ind);
_do_visit_flow_ml(ich, 0u);
this->Writer::_do_write('\n');
}
else
{
_do_visit_block(ich, next_level, do_indent);
}
}
do_indent = true;
ind = indent_to(do_indent * next_level);
}
}
}
template<class Writer>
void Emitter<Writer>::_do_visit_block(size_t node, size_t ilevel, size_t do_indent)
{
RYML_ASSERT(!m_tree->is_stream(node));
RYML_ASSERT(m_tree->is_container(node) || m_tree->is_doc(node));
RYML_ASSERT(m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)));
RepC ind = indent_to(do_indent * ilevel);
if(m_tree->is_doc(node))
{
_write_doc(node);
if(!m_tree->has_children(node))
return;
}
else if(m_tree->is_container(node))
{
RYML_ASSERT(m_tree->is_map(node) || m_tree->is_seq(node));
bool spc = false; // write a space
bool nl = false; // write a newline
if(m_tree->has_key(node))
{
this->Writer::_do_write(ind);
_writek(node, ilevel);
this->Writer::_do_write(':');
spc = true;
}
else if(!m_tree->is_root(node))
{
this->Writer::_do_write(ind);
this->Writer::_do_write('-');
spc = true;
}
if(m_tree->has_val_tag(node))
{
if(spc)
this->Writer::_do_write(' ');
_write_tag(m_tree->val_tag(node));
spc = true;
nl = true;
}
if(m_tree->has_val_anchor(node))
{
if(spc)
this->Writer::_do_write(' ');
this->Writer::_do_write('&');
this->Writer::_do_write(m_tree->val_anchor(node));
spc = true;
nl = true;
}
if(m_tree->has_children(node))
{
if(m_tree->has_key(node))
nl = true;
else
if(!m_tree->is_root(node) && !nl)
spc = true;
}
else
{
if(m_tree->is_seq(node))
this->Writer::_do_write(" []\n");
else if(m_tree->is_map(node))
this->Writer::_do_write(" {}\n");
return;
}
if(spc && !nl)
this->Writer::_do_write(' ');
do_indent = 0;
if(nl)
{
this->Writer::_do_write('\n');
do_indent = 1;
}
} // container
size_t next_level = ilevel + 1;
if(m_tree->is_root(node) || m_tree->is_doc(node))
next_level = ilevel; // do not indent at top level
_do_visit_block_container(node, next_level, do_indent);
}
C4_SUPPRESS_WARNING_MSVC_POP
template<class Writer>
void Emitter<Writer>::_do_visit_json(size_t id)
{
_RYML_CB_CHECK(m_tree->callbacks(), !m_tree->is_stream(id)); // JSON does not have streams
if(m_tree->is_keyval(id))
{
_writek_json(id);
this->Writer::_do_write(": ");
_writev_json(id);
}
else if(m_tree->is_val(id))
{
_writev_json(id);
}
else if(m_tree->is_container(id))
{
if(m_tree->has_key(id))
{
_writek_json(id);
this->Writer::_do_write(": ");
}
if(m_tree->is_seq(id))
this->Writer::_do_write('[');
else if(m_tree->is_map(id))
this->Writer::_do_write('{');
} // container
for(size_t ich = m_tree->first_child(id); ich != NONE; ich = m_tree->next_sibling(ich))
{
if(ich != m_tree->first_child(id))
this->Writer::_do_write(',');
_do_visit_json(ich);
}
if(m_tree->is_seq(id))
this->Writer::_do_write(']');
else if(m_tree->is_map(id))
this->Writer::_do_write('}');
}
template<class Writer>
void Emitter<Writer>::_write(NodeScalar const& C4_RESTRICT sc, NodeType flags, size_t ilevel)
{
if( ! sc.tag.empty())
{
_write_tag(sc.tag);
this->Writer::_do_write(' ');
}
if(flags.has_anchor())
{
RYML_ASSERT(flags.is_ref() != flags.has_anchor());
RYML_ASSERT( ! sc.anchor.empty());
this->Writer::_do_write('&');
this->Writer::_do_write(sc.anchor);
this->Writer::_do_write(' ');
}
else if(flags.is_ref())
{
if(sc.anchor != "<<")
this->Writer::_do_write('*');
this->Writer::_do_write(sc.anchor);
return;
}
// ensure the style flags only have one of KEY or VAL
_RYML_CB_ASSERT(m_tree->callbacks(), ((flags & (_WIP_KEY_STYLE|_WIP_VAL_STYLE)) == 0) || (((flags&_WIP_KEY_STYLE) == 0) != ((flags&_WIP_VAL_STYLE) == 0)));
auto style_marks = flags & (_WIP_KEY_STYLE|_WIP_VAL_STYLE);
if(style_marks & (_WIP_KEY_LITERAL|_WIP_VAL_LITERAL))
{
_write_scalar_literal(sc.scalar, ilevel, flags.has_key());
}
else if(style_marks & (_WIP_KEY_FOLDED|_WIP_VAL_FOLDED))
{
_write_scalar_folded(sc.scalar, ilevel, flags.has_key());
}
else if(style_marks & (_WIP_KEY_SQUO|_WIP_VAL_SQUO))
{
_write_scalar_squo(sc.scalar, ilevel);
}
else if(style_marks & (_WIP_KEY_DQUO|_WIP_VAL_DQUO))
{
_write_scalar_dquo(sc.scalar, ilevel);
}
else if(style_marks & (_WIP_KEY_PLAIN|_WIP_VAL_PLAIN))
{
_write_scalar_plain(sc.scalar, ilevel);
}
else if(!style_marks)
{
size_t first_non_nl = sc.scalar.first_not_of('\n');
bool all_newlines = first_non_nl == npos;
bool has_leading_ws = (!all_newlines) && sc.scalar.sub(first_non_nl).begins_with_any(" \t");
bool do_literal = ((!sc.scalar.empty() && all_newlines) || (has_leading_ws && !sc.scalar.trim(' ').empty()));
if(do_literal)
{
_write_scalar_literal(sc.scalar, ilevel, flags.has_key(), /*explicit_indentation*/has_leading_ws);
}
else
{
for(size_t i = 0; i < sc.scalar.len; ++i)
{
if(sc.scalar.str[i] == '\n')
{
_write_scalar_literal(sc.scalar, ilevel, flags.has_key(), /*explicit_indentation*/has_leading_ws);
goto wrote_special;
}
// todo: check for escaped characters requiring double quotes
}
_write_scalar(sc.scalar, flags.is_quoted());
wrote_special:
;
}
}
else
{
_RYML_CB_ERR(m_tree->callbacks(), "not implemented");
}
}
template<class Writer>
void Emitter<Writer>::_write_json(NodeScalar const& C4_RESTRICT sc, NodeType flags)
{
if(C4_UNLIKELY( ! sc.tag.empty()))
_RYML_CB_ERR(m_tree->callbacks(), "JSON does not have tags");
if(C4_UNLIKELY(flags.has_anchor()))
_RYML_CB_ERR(m_tree->callbacks(), "JSON does not have anchors");
_write_scalar_json(sc.scalar, flags.has_key(), flags.is_quoted());
}
#define _rymlindent_nextline() for(size_t lv = 0; lv < ilevel+1; ++lv) { this->Writer::_do_write(' '); this->Writer::_do_write(' '); }
template<class Writer>
void Emitter<Writer>::_write_scalar_literal(csubstr s, size_t ilevel, bool explicit_key, bool explicit_indentation)
{
if(explicit_key)
this->Writer::_do_write("? ");
csubstr trimmed = s.trimr("\n\r");
size_t numnewlines_at_end = s.len - trimmed.len - s.sub(trimmed.len).count('\r');
//
if(!explicit_indentation)
this->Writer::_do_write('|');
else
this->Writer::_do_write("|2");
//
if(numnewlines_at_end > 1 || (trimmed.len == 0 && s.len > 0)/*only newlines*/)
this->Writer::_do_write("+\n");
else if(numnewlines_at_end == 1)
this->Writer::_do_write('\n');
else
this->Writer::_do_write("-\n");
//
if(trimmed.len)
{
size_t pos = 0; // tracks the last character that was already written
for(size_t i = 0; i < trimmed.len; ++i)
{
if(trimmed[i] != '\n')
continue;
// write everything up to this point
csubstr since_pos = trimmed.range(pos, i+1); // include the newline
_rymlindent_nextline()
this->Writer::_do_write(since_pos);
pos = i+1; // already written
}
if(pos < trimmed.len)
{
_rymlindent_nextline()
this->Writer::_do_write(trimmed.sub(pos));
}
if(numnewlines_at_end)
{
this->Writer::_do_write('\n');
--numnewlines_at_end;
}
}
for(size_t i = 0; i < numnewlines_at_end; ++i)
{
_rymlindent_nextline()
if(i+1 < numnewlines_at_end || explicit_key)
this->Writer::_do_write('\n');
}
if(explicit_key && !numnewlines_at_end)
this->Writer::_do_write('\n');
}
template<class Writer>
void Emitter<Writer>::_write_scalar_folded(csubstr s, size_t ilevel, bool explicit_key)
{
if(explicit_key)
{
this->Writer::_do_write("? ");
}
RYML_ASSERT(s.find("\r") == csubstr::npos);
csubstr trimmed = s.trimr('\n');
size_t numnewlines_at_end = s.len - trimmed.len;
if(numnewlines_at_end == 0)
{
this->Writer::_do_write(">-\n");
}
else if(numnewlines_at_end == 1)
{
this->Writer::_do_write(">\n");
}
else if(numnewlines_at_end > 1)
{
this->Writer::_do_write(">+\n");
}
if(trimmed.len)
{
size_t pos = 0; // tracks the last character that was already written
for(size_t i = 0; i < trimmed.len; ++i)
{
if(trimmed[i] != '\n')
continue;
// write everything up to this point
csubstr since_pos = trimmed.range(pos, i+1); // include the newline
pos = i+1; // because of the newline
_rymlindent_nextline()
this->Writer::_do_write(since_pos);
this->Writer::_do_write('\n'); // write the newline twice
}
if(pos < trimmed.len)
{
_rymlindent_nextline()
this->Writer::_do_write(trimmed.sub(pos));
}
if(numnewlines_at_end)
{
this->Writer::_do_write('\n');
--numnewlines_at_end;
}
}
for(size_t i = 0; i < numnewlines_at_end; ++i)
{
_rymlindent_nextline()
if(i+1 < numnewlines_at_end || explicit_key)
this->Writer::_do_write('\n');
}
if(explicit_key && !numnewlines_at_end)
this->Writer::_do_write('\n');
}
template<class Writer>
void Emitter<Writer>::_write_scalar_squo(csubstr s, size_t ilevel)
{
size_t pos = 0; // tracks the last character that was already written
this->Writer::_do_write('\'');
for(size_t i = 0; i < s.len; ++i)
{
if(s[i] == '\n')
{
csubstr sub = s.range(pos, i+1);
this->Writer::_do_write(sub); // write everything up to (including) this char
this->Writer::_do_write('\n'); // write the character again
if(i + 1 < s.len)
_rymlindent_nextline() // indent the next line
pos = i+1;
}
else if(s[i] == '\'')
{
csubstr sub = s.range(pos, i+1);
this->Writer::_do_write(sub); // write everything up to (including) this char
this->Writer::_do_write('\''); // write the character again
pos = i+1;
}
}
// write missing characters at the end of the string
if(pos < s.len)
this->Writer::_do_write(s.sub(pos));
this->Writer::_do_write('\'');
}
template<class Writer>
void Emitter<Writer>::_write_scalar_dquo(csubstr s, size_t ilevel)
{
size_t pos = 0; // tracks the last character that was already written
this->Writer::_do_write('"');
for(size_t i = 0; i < s.len; ++i)
{
const char curr = s.str[i];
if(curr == '"' || curr == '\\')
{
csubstr sub = s.range(pos, i);
this->Writer::_do_write(sub); // write everything up to (excluding) this char
this->Writer::_do_write('\\'); // write the escape
this->Writer::_do_write(curr); // write the char
pos = i+1;
}
else if(s[i] == '\n')
{
csubstr sub = s.range(pos, i+1);
this->Writer::_do_write(sub); // write everything up to (including) this newline
this->Writer::_do_write('\n'); // write the newline again
if(i + 1 < s.len)
_rymlindent_nextline() // indent the next line
pos = i+1;
if(i+1 < s.len) // escape leading whitespace after the newline
{
const char next = s.str[i+1];
if(next == ' ' || next == '\t')
this->Writer::_do_write('\\');
}
}
else if(curr == ' ' || curr == '\t')
{
// escape trailing whitespace before a newline
size_t next = s.first_not_of(" \t\r", i);
if(next != npos && s[next] == '\n')
{
csubstr sub = s.range(pos, i);
this->Writer::_do_write(sub); // write everything up to (excluding) this char
this->Writer::_do_write('\\'); // escape the whitespace
pos = i;
}
}
else if(C4_UNLIKELY(curr == '\r'))
{
csubstr sub = s.range(pos, i);
this->Writer::_do_write(sub); // write everything up to (excluding) this char
this->Writer::_do_write("\\r"); // write the escaped char
pos = i+1;
}
}
// write missing characters at the end of the string
if(pos < s.len)
{
csubstr sub = s.sub(pos);
this->Writer::_do_write(sub);
}
this->Writer::_do_write('"');
}
template<class Writer>
void Emitter<Writer>::_write_scalar_plain(csubstr s, size_t ilevel)
{
size_t pos = 0; // tracks the last character that was already written
for(size_t i = 0; i < s.len; ++i)
{
const char curr = s.str[i];
if(curr == '\n')
{
csubstr sub = s.range(pos, i+1);
this->Writer::_do_write(sub); // write everything up to (including) this newline
this->Writer::_do_write('\n'); // write the newline again
if(i + 1 < s.len)
_rymlindent_nextline() // indent the next line
pos = i+1;
}
}
// write missing characters at the end of the string
if(pos < s.len)
{
csubstr sub = s.sub(pos);
this->Writer::_do_write(sub);
}
}
#undef _rymlindent_nextline
template<class Writer>
void Emitter<Writer>::_write_scalar(csubstr s, bool was_quoted)
{
// this block of code needed to be moved to before the needs_quotes
// assignment to work around a g++ optimizer bug where (s.str != nullptr)
// was evaluated as true even if s.str was actually a nullptr (!!!)
if(s.len == size_t(0))
{
if(was_quoted || s.str != nullptr)
this->Writer::_do_write("''");
return;
}
const bool needs_quotes = (
was_quoted
||
(
( ! s.is_number())
&&
(
// has leading whitespace
// looks like reference or anchor
// would be treated as a directive
// see https://www.yaml.info/learn/quote.html#noplain
s.begins_with_any(" \n\t\r*&%@`")
||
s.begins_with("<<")
||
// has trailing whitespace
s.ends_with_any(" \n\t\r")
||
// has special chars
(s.first_of("#:-?,\n{}[]'\"") != npos)
)
)
);
if( ! needs_quotes)
{
this->Writer::_do_write(s);
}
else
{
const bool has_dquotes = s.first_of( '"') != npos;
const bool has_squotes = s.first_of('\'') != npos;
if(!has_squotes && has_dquotes)
{
this->Writer::_do_write('\'');
this->Writer::_do_write(s);
this->Writer::_do_write('\'');
}
else if(has_squotes && !has_dquotes)
{
RYML_ASSERT(s.count('\n') == 0);
this->Writer::_do_write('"');
this->Writer::_do_write(s);
this->Writer::_do_write('"');
}
else
{
_write_scalar_squo(s, /*FIXME FIXME FIXME*/0);
}
}
}
template<class Writer>
void Emitter<Writer>::_write_scalar_json(csubstr s, bool as_key, bool use_quotes)
{
if((!use_quotes)
// json keys require quotes
&& (!as_key)
&& (
// do not quote special cases
(s == "true" || s == "false" || s == "null")
|| (
// do not quote numbers
(s.is_number()
&& (
// quote integral numbers if they have a leading 0
// https://github.com/biojppm/rapidyaml/issues/291
(!(s.len > 1 && s.begins_with('0')))
// do not quote reals with leading 0
// https://github.com/biojppm/rapidyaml/issues/313
|| (s.find('.') != csubstr::npos) ))
)
)
)
{
this->Writer::_do_write(s);
}
else
{
size_t pos = 0;
this->Writer::_do_write('"');
for(size_t i = 0; i < s.len; ++i)
{
switch(s.str[i])
{
case '"':
this->Writer ::_do_write(s.range(pos, i));
this->Writer ::_do_write("\\\"");
pos = i + 1;
break;
case '\n':
this->Writer ::_do_write(s.range(pos, i));
this->Writer ::_do_write("\\n");
pos = i + 1;
break;
case '\t':
this->Writer ::_do_write(s.range(pos, i));
this->Writer ::_do_write("\\t");
pos = i + 1;
break;
case '\\':
this->Writer ::_do_write(s.range(pos, i));
this->Writer ::_do_write("\\\\");
pos = i + 1;
break;
case '\r':
this->Writer ::_do_write(s.range(pos, i));
this->Writer ::_do_write("\\r");
pos = i + 1;
break;
case '\b':
this->Writer ::_do_write(s.range(pos, i));
this->Writer ::_do_write("\\b");
pos = i + 1;
break;
case '\f':
this->Writer ::_do_write(s.range(pos, i));
this->Writer ::_do_write("\\f");
pos = i + 1;
break;
}
}
if(pos < s.len)
{
csubstr sub = s.sub(pos);
this->Writer::_do_write(sub);
}
this->Writer::_do_write('"');
}
}
} // namespace yml
} // namespace c4
#endif /* _C4_YML_EMIT_DEF_HPP_ */

View File

@ -0,0 +1,534 @@
#ifndef _C4_YML_EMIT_HPP_
#define _C4_YML_EMIT_HPP_
/** @file emit.hpp Utilities to emit YAML and JSON. */
#ifndef _C4_YML_WRITER_HPP_
#include "./writer.hpp"
#endif
#ifndef _C4_YML_TREE_HPP_
#include "./tree.hpp"
#endif
#ifndef _C4_YML_NODE_HPP_
#include "./node.hpp"
#endif
#ifdef emit
#error "emit is defined, likely from a Qt include. This will cause a compilation error. See https://github.com/biojppm/rapidyaml/issues/120"
#endif
#define RYML_DEPRECATE_EMIT \
RYML_DEPRECATED("use emit_yaml() instead. See https://github.com/biojppm/rapidyaml/issues/120")
#define RYML_DEPRECATE_EMITRS \
RYML_DEPRECATED("use emitrs_yaml() instead. See https://github.com/biojppm/rapidyaml/issues/120")
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
namespace c4 {
namespace yml {
/** @addtogroup doc_emit
*
* @{
*/
// fwd declarations
template<class Writer> class Emitter;
template<class OStream>
using EmitterOStream = Emitter<WriterOStream<OStream>>;
using EmitterFile = Emitter<WriterFile>;
using EmitterBuf = Emitter<WriterBuf>;
/** Specifies the type of content to emit */
typedef enum {
EMIT_YAML = 0, ///< emit YAML
EMIT_JSON = 1 ///< emit JSON
} EmitType_e;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** A stateful emitter, for use with a writer such as @ref WriterBuf,
* @ref WriterFile, or @ref WriterOStream */
template<class Writer>
class Emitter : public Writer
{
public:
/** Construct the emitter and its internal Writer state. Every
* parameter is forwarded to the constructor of the writer. */
template<class ...Args>
Emitter(Args &&...args) : Writer(std::forward<Args>(args)...), m_tree() {}
/** emit!
*
* When writing to a buffer, returns a substr of the emitted YAML.
* If the given buffer has insufficient space, the returned substr
* will be null and its size will be the needed space. Whatever
* the size of the buffer, it is guaranteed that no writes are
* done past its end.
*
* When writing to a file, the returned substr will be null, but its
* length will be set to the number of bytes written.
*
* @param type specify what to emit
* @param t the tree to emit
* @param id the id of the node to emit
* @param error_on_excess when true, an error is raised when the
* output buffer is too small for the emitted YAML/JSON
* */
substr emit_as(EmitType_e type, Tree const& t, size_t id, bool error_on_excess);
/** emit starting at the root node */
substr emit_as(EmitType_e type, Tree const& t, bool error_on_excess=true);
/** emit the given node */
substr emit_as(EmitType_e type, ConstNodeRef const& n, bool error_on_excess=true);
private:
Tree const* C4_RESTRICT m_tree;
void _emit_yaml(size_t id);
void _do_visit_flow_sl(size_t id, size_t ilevel=0);
void _do_visit_flow_ml(size_t id, size_t ilevel=0, size_t do_indent=1);
void _do_visit_block(size_t id, size_t ilevel=0, size_t do_indent=1);
void _do_visit_block_container(size_t id, size_t next_level, size_t do_indent);
void _do_visit_json(size_t id);
private:
void _write(NodeScalar const& C4_RESTRICT sc, NodeType flags, size_t level);
void _write_json(NodeScalar const& C4_RESTRICT sc, NodeType flags);
void _write_doc(size_t id);
void _write_scalar(csubstr s, bool was_quoted);
void _write_scalar_json(csubstr s, bool as_key, bool was_quoted);
void _write_scalar_literal(csubstr s, size_t level, bool as_key, bool explicit_indentation=false);
void _write_scalar_folded(csubstr s, size_t level, bool as_key);
void _write_scalar_squo(csubstr s, size_t level);
void _write_scalar_dquo(csubstr s, size_t level);
void _write_scalar_plain(csubstr s, size_t level);
void _write_tag(csubstr tag)
{
if(!tag.begins_with('!'))
this->Writer::_do_write('!');
this->Writer::_do_write(tag);
}
enum : type_bits {
_keysc = (KEY|KEYREF|KEYANCH|KEYQUO|_WIP_KEY_STYLE) | ~(VAL|VALREF|VALANCH|VALQUO|_WIP_VAL_STYLE),
_valsc = ~(KEY|KEYREF|KEYANCH|KEYQUO|_WIP_KEY_STYLE) | (VAL|VALREF|VALANCH|VALQUO|_WIP_VAL_STYLE),
_keysc_json = (KEY) | ~(VAL),
_valsc_json = ~(KEY) | (VAL),
};
C4_ALWAYS_INLINE void _writek(size_t id, size_t level) { _write(m_tree->keysc(id), m_tree->_p(id)->m_type.type & ~_valsc, level); }
C4_ALWAYS_INLINE void _writev(size_t id, size_t level) { _write(m_tree->valsc(id), m_tree->_p(id)->m_type.type & ~_keysc, level); }
C4_ALWAYS_INLINE void _writek_json(size_t id) { _write_json(m_tree->keysc(id), m_tree->_p(id)->m_type.type & ~(VAL)); }
C4_ALWAYS_INLINE void _writev_json(size_t id) { _write_json(m_tree->valsc(id), m_tree->_p(id)->m_type.type & ~(KEY)); }
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** @defgroup doc_emit_to_file Emit to file
*
* @{
*/
/** emit YAML to the given file. A null file defaults to stdout.
* Return the number of bytes written. */
inline size_t emit_yaml(Tree const& t, size_t id, FILE *f)
{
EmitterFile em(f);
return em.emit_as(EMIT_YAML, t, id, /*error_on_excess*/true).len;
}
/** emit JSON to the given file. A null file defaults to stdout.
* Return the number of bytes written. */
inline size_t emit_json(Tree const& t, size_t id, FILE *f)
{
EmitterFile em(f);
return em.emit_as(EMIT_JSON, t, id, /*error_on_excess*/true).len;
}
/** emit YAML to the given file. A null file defaults to stdout.
* Return the number of bytes written.
* @overload */
inline size_t emit_yaml(Tree const& t, FILE *f=nullptr)
{
EmitterFile em(f);
return em.emit_as(EMIT_YAML, t, /*error_on_excess*/true).len;
}
/** emit JSON to the given file. A null file defaults to stdout.
* Return the number of bytes written.
* @overload */
inline size_t emit_json(Tree const& t, FILE *f=nullptr)
{
EmitterFile em(f);
return em.emit_as(EMIT_JSON, t, /*error_on_excess*/true).len;
}
/** emit YAML to the given file. A null file defaults to stdout.
* Return the number of bytes written.
* @overload */
inline size_t emit_yaml(ConstNodeRef const& r, FILE *f=nullptr)
{
EmitterFile em(f);
return em.emit_as(EMIT_YAML, r, /*error_on_excess*/true).len;
}
/** emit JSON to the given file. A null file defaults to stdout.
* Return the number of bytes written.
* @overload */
inline size_t emit_json(ConstNodeRef const& r, FILE *f=nullptr)
{
EmitterFile em(f);
return em.emit_as(EMIT_JSON, r, /*error_on_excess*/true).len;
}
/** @} */
//-----------------------------------------------------------------------------
/** @defgroup doc_emit_to_ostream Emit to an STL-like ostream
*
* @{
*/
/** mark a tree or node to be emitted as json when using @ref operator<< . For example:
*
* ```cpp
* Tree t = parse_in_arena("{foo: bar}");
* std::cout << t; // emits YAML
* std::cout << as_json(t); // emits JSON
* ```
*
* @see @ref operator<< */
struct as_json
{
Tree const* tree;
size_t node;
as_json(Tree const& t) : tree(&t), node(t.empty() ? NONE : t.root_id()) {}
as_json(Tree const& t, size_t id) : tree(&t), node(id) {}
as_json(ConstNodeRef const& n) : tree(n.tree()), node(n.id()) {}
};
/** emit YAML to an STL-like ostream */
template<class OStream>
inline OStream& operator<< (OStream& s, Tree const& t)
{
EmitterOStream<OStream> em(s);
em.emit_as(EMIT_YAML, t);
return s;
}
/** emit YAML to an STL-like ostream
* @overload */
template<class OStream>
inline OStream& operator<< (OStream& s, ConstNodeRef const& n)
{
EmitterOStream<OStream> em(s);
em.emit_as(EMIT_YAML, n);
return s;
}
/** emit json to an STL-like stream */
template<class OStream>
inline OStream& operator<< (OStream& s, as_json const& j)
{
EmitterOStream<OStream> em(s);
em.emit_as(EMIT_JSON, *j.tree, j.node, true);
return s;
}
/** @} */
//-----------------------------------------------------------------------------
/** @defgroup doc_emit_to_buffer Emit to memory buffer
*
* @{
*/
/** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload */
inline substr emit_yaml(Tree const& t, size_t id, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
return em.emit_as(EMIT_YAML, t, id, error_on_excess);
}
/** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload */
inline substr emit_json(Tree const& t, size_t id, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
return em.emit_as(EMIT_JSON, t, id, error_on_excess);
}
/** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload */
inline substr emit_yaml(Tree const& t, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
return em.emit_as(EMIT_YAML, t, error_on_excess);
}
/** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload */
inline substr emit_json(Tree const& t, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
return em.emit_as(EMIT_JSON, t, error_on_excess);
}
/** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload
*/
inline substr emit_yaml(ConstNodeRef const& r, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
return em.emit_as(EMIT_YAML, r, error_on_excess);
}
/** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload
*/
inline substr emit_json(ConstNodeRef const& r, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
return em.emit_as(EMIT_JSON, r, error_on_excess);
}
//-----------------------------------------------------------------------------
/** emit+resize: emit YAML to the given `std::string`/`std::vector`-like
* container, resizing it as needed to fit the emitted YAML. */
template<class CharOwningContainer>
substr emitrs_yaml(Tree const& t, size_t id, CharOwningContainer * cont)
{
substr buf = to_substr(*cont);
substr ret = emit_yaml(t, id, buf, /*error_on_excess*/false);
if(ret.str == nullptr && ret.len > 0)
{
cont->resize(ret.len);
buf = to_substr(*cont);
ret = emit_yaml(t, id, buf, /*error_on_excess*/true);
}
return ret;
}
/** emit+resize: emit JSON to the given `std::string`/`std::vector`-like
* container, resizing it as needed to fit the emitted JSON. */
template<class CharOwningContainer>
substr emitrs_json(Tree const& t, size_t id, CharOwningContainer * cont)
{
substr buf = to_substr(*cont);
substr ret = emit_json(t, id, buf, /*error_on_excess*/false);
if(ret.str == nullptr && ret.len > 0)
{
cont->resize(ret.len);
buf = to_substr(*cont);
ret = emit_json(t, id, buf, /*error_on_excess*/true);
}
return ret;
}
/** emit+resize: emit YAML to the given `std::string`/`std::vector`-like
* container, resizing it as needed to fit the emitted YAML. */
template<class CharOwningContainer>
CharOwningContainer emitrs_yaml(Tree const& t, size_t id)
{
CharOwningContainer c;
emitrs_yaml(t, id, &c);
return c;
}
/** emit+resize: emit JSON to the given `std::string`/`std::vector`-like
* container, resizing it as needed to fit the emitted JSON. */
template<class CharOwningContainer>
CharOwningContainer emitrs_json(Tree const& t, size_t id)
{
CharOwningContainer c;
emitrs_json(t, id, &c);
return c;
}
/** emit+resize: YAML to the given `std::string`/`std::vector`-like
* container, resizing it as needed to fit the emitted YAML. */
template<class CharOwningContainer>
substr emitrs_yaml(Tree const& t, CharOwningContainer * cont)
{
if(t.empty())
return {};
return emitrs_yaml(t, t.root_id(), cont);
}
/** emit+resize: JSON to the given `std::string`/`std::vector`-like
* container, resizing it as needed to fit the emitted JSON. */
template<class CharOwningContainer>
substr emitrs_json(Tree const& t, CharOwningContainer * cont)
{
if(t.empty())
return {};
return emitrs_json(t, t.root_id(), cont);
}
/** emit+resize: YAML to the given `std::string`/`std::vector`-like container,
* resizing it as needed to fit the emitted YAML. */
template<class CharOwningContainer>
CharOwningContainer emitrs_yaml(Tree const& t)
{
CharOwningContainer c;
if(t.empty())
return c;
emitrs_yaml(t, t.root_id(), &c);
return c;
}
/** emit+resize: JSON to the given `std::string`/`std::vector`-like container,
* resizing it as needed to fit the emitted JSON. */
template<class CharOwningContainer>
CharOwningContainer emitrs_json(Tree const& t)
{
CharOwningContainer c;
if(t.empty())
return c;
emitrs_json(t, t.root_id(), &c);
return c;
}
/** emit+resize: YAML to the given `std::string`/`std::vector`-like container,
* resizing it as needed to fit the emitted YAML. */
template<class CharOwningContainer>
substr emitrs_yaml(ConstNodeRef const& n, CharOwningContainer * cont)
{
_RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
return emitrs_yaml(*n.tree(), n.id(), cont);
}
/** emit+resize: JSON to the given `std::string`/`std::vector`-like container,
* resizing it as needed to fit the emitted JSON. */
template<class CharOwningContainer>
substr emitrs_json(ConstNodeRef const& n, CharOwningContainer * cont)
{
_RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
return emitrs_json(*n.tree(), n.id(), cont);
}
/** emit+resize: YAML to the given `std::string`/`std::vector`-like container,
* resizing it as needed to fit the emitted YAML. */
template<class CharOwningContainer>
CharOwningContainer emitrs_yaml(ConstNodeRef const& n)
{
_RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
CharOwningContainer c;
emitrs_yaml(*n.tree(), n.id(), &c);
return c;
}
/** emit+resize: JSON to the given `std::string`/`std::vector`-like container,
* resizing it as needed to fit the emitted JSON. */
template<class CharOwningContainer>
CharOwningContainer emitrs_json(ConstNodeRef const& n)
{
_RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
CharOwningContainer c;
emitrs_json(*n.tree(), n.id(), &c);
return c;
}
/** @} */
//-----------------------------------------------------------------------------
/** @cond dev */
RYML_DEPRECATE_EMIT inline size_t emit(Tree const& t, size_t id, FILE *f)
{
return emit_yaml(t, id, f);
}
RYML_DEPRECATE_EMIT inline size_t emit(Tree const& t, FILE *f=nullptr)
{
return emit_yaml(t, f);
}
RYML_DEPRECATE_EMIT inline size_t emit(ConstNodeRef const& r, FILE *f=nullptr)
{
return emit_yaml(r, f);
}
RYML_DEPRECATE_EMIT inline substr emit(Tree const& t, size_t id, substr buf, bool error_on_excess=true)
{
return emit_yaml(t, id, buf, error_on_excess);
}
RYML_DEPRECATE_EMIT inline substr emit(Tree const& t, substr buf, bool error_on_excess=true)
{
return emit_yaml(t, buf, error_on_excess);
}
RYML_DEPRECATE_EMIT inline substr emit(ConstNodeRef const& r, substr buf, bool error_on_excess=true)
{
return emit_yaml(r, buf, error_on_excess);
}
template<class CharOwningContainer>
RYML_DEPRECATE_EMITRS substr emitrs(Tree const& t, size_t id, CharOwningContainer * cont)
{
return emitrs_yaml(t, id, cont);
}
template<class CharOwningContainer>
RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(Tree const& t, size_t id)
{
return emitrs_yaml<CharOwningContainer>(t, id);
}
template<class CharOwningContainer>
RYML_DEPRECATE_EMITRS substr emitrs(Tree const& t, CharOwningContainer * cont)
{
return emitrs_yaml(t, cont);
}
template<class CharOwningContainer>
RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(Tree const& t)
{
return emitrs_yaml<CharOwningContainer>(t);
}
template<class CharOwningContainer>
RYML_DEPRECATE_EMITRS substr emitrs(ConstNodeRef const& n, CharOwningContainer * cont)
{
return emitrs_yaml(n, cont);
}
template<class CharOwningContainer>
RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(ConstNodeRef const& n)
{
return emitrs_yaml<CharOwningContainer>(n);
}
/** @endcond */
} // namespace yml
} // namespace c4
#undef RYML_DEPRECATE_EMIT
#undef RYML_DEPRECATE_EMITRS
#include "c4/yml/emit.def.hpp"
#endif /* _C4_YML_EMIT_HPP_ */

View File

@ -0,0 +1,18 @@
#ifndef C4_YML_EXPORT_HPP_
#define C4_YML_EXPORT_HPP_
#ifdef _WIN32
#ifdef RYML_SHARED
#ifdef RYML_EXPORTS
#define RYML_EXPORT __declspec(dllexport)
#else
#define RYML_EXPORT __declspec(dllimport)
#endif
#else
#define RYML_EXPORT
#endif
#else
#define RYML_EXPORT
#endif
#endif /* C4_YML_EXPORT_HPP_ */

1586
3rdparty/rapidyaml/include/c4/yml/node.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,737 @@
#ifndef _C4_YML_PARSE_HPP_
#define _C4_YML_PARSE_HPP_
/** @file parse.hpp Utilities to parse YAML and JSON */
#ifndef _C4_YML_TREE_HPP_
#include "c4/yml/tree.hpp"
#endif
#ifndef _C4_YML_NODE_HPP_
#include "c4/yml/node.hpp"
#endif
#ifndef _C4_YML_DETAIL_STACK_HPP_
#include "c4/yml/detail/stack.hpp"
#endif
#include <stdarg.h>
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4251/*needs to have dll-interface to be used by clients of struct*/)
#endif
namespace c4 {
namespace yml {
/** @addtogroup doc_parse
*
* @{
*/
/** Options to initialize a @ref Parser object. */
struct RYML_EXPORT ParserOptions
{
private:
typedef enum : uint32_t {
LOCATIONS = (1 << 0),
DEFAULTS = 0,
} Flags_e;
uint32_t flags = DEFAULTS;
public:
ParserOptions() = default;
/** @name source location tracking */
/** @{ */
/** enable/disable source location tracking */
ParserOptions& locations(bool enabled)
{
if(enabled)
flags |= LOCATIONS;
else
flags &= ~LOCATIONS;
return *this;
}
bool locations() const { return (flags & LOCATIONS) != 0u; }
/** @} */
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** A reusable object to parse YAML/JSON and create the ryml @ref Tree. */
class RYML_EXPORT Parser
{
public:
/** @name construction and assignment */
/** @{ */
Parser(Callbacks const& cb, ParserOptions opts={});
Parser(ParserOptions opts={}) : Parser(get_callbacks(), opts) {}
~Parser();
Parser(Parser &&);
Parser(Parser const&);
Parser& operator=(Parser &&);
Parser& operator=(Parser const&);
/** @} */
public:
/** @name modifiers */
/** @{ */
/** Reserve a certain capacity for the parsing stack.
* This should be larger than the expected depth of the parsed
* YAML tree.
*
* The parsing stack is the only (potential) heap memory used by
* the parser.
*
* If the requested capacity is below the default
* stack size of 16, the memory is used directly in the parser
* object; otherwise it will be allocated from the heap.
*
* @note this reserves memory only for the parser itself; all the
* allocations for the parsed tree will go through the tree's
* allocator.
*
* @note the tree and the arena can (and should) also be reserved. */
void reserve_stack(size_t capacity)
{
m_stack.reserve(capacity);
}
/** Reserve a certain capacity for the array used to track node
* locations in the source buffer. */
void reserve_locations(size_t num_source_lines)
{
_resize_locations(num_source_lines);
}
/** Reserve a certain capacity for the character arena used to
* filter scalars. */
void reserve_filter_arena(size_t num_characters)
{
_resize_filter_arena(num_characters);
}
/** @} */
public:
/** @name getters and modifiers */
/** @{ */
/** Get the current callbacks in the parser. */
Callbacks callbacks() const { return m_stack.m_callbacks; }
/** Get the name of the latest file parsed by this object. */
csubstr filename() const { return m_file; }
/** Get the latest YAML buffer parsed by this object. */
csubstr source() const { return m_buf; }
size_t stack_capacity() const { return m_stack.capacity(); }
size_t locations_capacity() const { return m_newline_offsets_capacity; }
size_t filter_arena_capacity() const { return m_filter_arena.len; }
ParserOptions const& options() const { return m_options; }
/** @} */
public:
/** @name parse_in_place
*
* parse a mutable buffer in situ, potentially mutating it.
*/
/** @{ */
/** Create a new tree and parse into its root.
* The tree is created with the callbacks currently in the parser. */
Tree parse_in_place(csubstr filename, substr src)
{
Tree t(callbacks());
t.reserve(_estimate_capacity(src));
this->parse_in_place(filename, src, &t, t.root_id());
return t;
}
/** Parse into an existing tree, starting at its root node.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
void parse_in_place(csubstr filename, substr src, Tree *t)
{
this->parse_in_place(filename, src, t, t->root_id());
}
/** Parse into an existing node.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
void parse_in_place(csubstr filename, substr src, Tree *t, size_t node_id);
// ^^^^^^^^^^^^^ this is the workhorse overload; everything else is syntactic candy
/** Parse into an existing node.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
void parse_in_place(csubstr filename, substr src, NodeRef node)
{
this->parse_in_place(filename, src, node.tree(), node.id());
}
RYML_DEPRECATED("use parse_in_place() instead") Tree parse(csubstr filename, substr src) { return parse_in_place(filename, src); }
RYML_DEPRECATED("use parse_in_place() instead") void parse(csubstr filename, substr src, Tree *t) { parse_in_place(filename, src, t); }
RYML_DEPRECATED("use parse_in_place() instead") void parse(csubstr filename, substr src, Tree *t, size_t node_id) { parse_in_place(filename, src, t, node_id); }
RYML_DEPRECATED("use parse_in_place() instead") void parse(csubstr filename, substr src, NodeRef node) { parse_in_place(filename, src, node); }
/** @} */
public:
/** @name parse_in_arena
*
* copy the YAML source buffer to the tree's arena, then parse the
* copy in situ
*
* @note overloads receiving a substr YAML buffer are intentionally
* left undefined, such that calling parse_in_arena() with a substr
* will cause a linker error. This is to prevent an accidental
* copy of the source buffer to the tree's arena, because substr
* is implicitly convertible to csubstr. If you really intend to parse
* a mutable buffer in the tree's arena, convert it first to immutable
* by assigning the substr to a csubstr prior to calling parse_in_arena().
* This is not needed for parse_in_place() because csubstr is not
* implicitly convertible to substr. */
/** @{ */
// READ THE NOTE ABOVE!
#define RYML_DONT_PARSE_SUBSTR_IN_ARENA "Do not pass a (mutable) substr to parse_in_arena(); if you have a substr, it should be parsed in place. Consider using parse_in_place() instead, or convert the buffer to csubstr prior to calling. This function is deliberately left undefined and will cause a linker error."
/** @cond dev */
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) Tree parse_in_arena(csubstr filename, substr csrc);
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr csrc, Tree *t);
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr csrc, Tree *t, size_t node_id);
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr csrc, NodeRef node);
/** @endcond */
/** Create a new tree and parse into its root.
* The immutable YAML source is first copied to the tree's arena,
* and parsed from there.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
Tree parse_in_arena(csubstr filename, csubstr csrc)
{
Tree t(callbacks());
substr src = t.copy_to_arena(csrc);
t.reserve(_estimate_capacity(csrc));
this->parse_in_place(filename, src, &t, t.root_id());
return t;
}
/** Parse into an existing tree, starting at its root node.
* The immutable YAML source is first copied to the tree's arena,
* and parsed from there.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
void parse_in_arena(csubstr filename, csubstr csrc, Tree *t)
{
substr src = t->copy_to_arena(csrc);
this->parse_in_place(filename, src, t, t->root_id());
}
/** Parse into a specific node in an existing tree.
* The immutable YAML source is first copied to the tree's arena,
* and parsed from there.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
void parse_in_arena(csubstr filename, csubstr csrc, Tree *t, size_t node_id)
{
substr src = t->copy_to_arena(csrc);
this->parse_in_place(filename, src, t, node_id);
}
/** Parse into a specific node in an existing tree.
* The immutable YAML source is first copied to the tree's arena,
* and parsed from there.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
void parse_in_arena(csubstr filename, csubstr csrc, NodeRef node)
{
substr src = node.tree()->copy_to_arena(csrc);
this->parse_in_place(filename, src, node.tree(), node.id());
}
/** @cond dev */
RYML_DEPRECATED("use parse_in_arena() instead") Tree parse(csubstr filename, csubstr csrc) { return parse_in_arena(filename, csrc); }
RYML_DEPRECATED("use parse_in_arena() instead") void parse(csubstr filename, csubstr csrc, Tree *t) { parse_in_arena(filename, csrc, t); }
RYML_DEPRECATED("use parse_in_arena() instead") void parse(csubstr filename, csubstr csrc, Tree *t, size_t node_id) { parse_in_arena(filename, csrc, t, node_id); }
RYML_DEPRECATED("use parse_in_arena() instead") void parse(csubstr filename, csubstr csrc, NodeRef node) { parse_in_arena(filename, csrc, node); }
/** @endcond */
/** @} */
public:
/** @name locations */
/** @{ */
/** Get the location of a node of the last tree to be parsed by this parser. */
Location location(Tree const& tree, size_t node_id) const;
/** Get the location of a node of the last tree to be parsed by this parser. */
Location location(ConstNodeRef node) const;
/** Get the string starting at a particular location, to the end
* of the parsed source buffer. */
csubstr location_contents(Location const& loc) const;
/** Given a pointer to a buffer position, get the location. @p val
* must be pointing to somewhere in the source buffer that was
* last parsed by this object. */
Location val_location(const char *val) const;
/** @} */
private:
typedef enum {
BLOCK_LITERAL, //!< keep newlines (|)
BLOCK_FOLD //!< replace newline with single space (>)
} BlockStyle_e;
typedef enum {
CHOMP_CLIP, //!< single newline at end (default)
CHOMP_STRIP, //!< no newline at end (-)
CHOMP_KEEP //!< all newlines from end (+)
} BlockChomp_e;
private:
using flag_t = int;
static size_t _estimate_capacity(csubstr src) { size_t c = _count_nlines(src); c = c >= 16 ? c : 16; return c; }
void _reset();
bool _finished_file() const;
bool _finished_line() const;
csubstr _peek_next_line(size_t pos=npos) const;
bool _advance_to_peeked();
void _scan_line();
csubstr _slurp_doc_scalar();
/**
* @param [out] quoted
* Will only be written to if this method returns true.
* Will be set to true if the scanned scalar was quoted, by '', "", > or |.
*/
bool _scan_scalar_seq_blck(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted);
bool _scan_scalar_map_blck(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted);
bool _scan_scalar_seq_flow(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted);
bool _scan_scalar_map_flow(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted);
bool _scan_scalar_unk(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted);
csubstr _scan_comment();
csubstr _scan_squot_scalar();
csubstr _scan_dquot_scalar();
csubstr _scan_block();
substr _scan_plain_scalar_blck(csubstr currscalar, csubstr peeked_line, size_t indentation);
substr _scan_plain_scalar_flow(csubstr currscalar, csubstr peeked_line);
substr _scan_complex_key(csubstr currscalar, csubstr peeked_line);
csubstr _scan_to_next_nonempty_line(size_t indentation);
csubstr _extend_scanned_scalar(csubstr currscalar);
csubstr _filter_squot_scalar(const substr s);
csubstr _filter_dquot_scalar(substr s);
csubstr _filter_plain_scalar(substr s, size_t indentation);
csubstr _filter_block_scalar(substr s, BlockStyle_e style, BlockChomp_e chomp, size_t indentation);
template<bool backslash_is_escape, bool keep_trailing_whitespace>
bool _filter_nl(substr scalar, size_t *C4_RESTRICT pos, size_t *C4_RESTRICT filter_arena_pos, size_t indentation);
template<bool keep_trailing_whitespace>
void _filter_ws(substr scalar, size_t *C4_RESTRICT pos, size_t *C4_RESTRICT filter_arena_pos);
bool _apply_chomp(substr buf, size_t *C4_RESTRICT pos, BlockChomp_e chomp);
void _handle_finished_file();
void _handle_line();
bool _handle_indentation();
bool _handle_unk();
bool _handle_map_flow();
bool _handle_map_blck();
bool _handle_seq_flow();
bool _handle_seq_blck();
bool _handle_top();
bool _handle_types();
bool _handle_key_anchors_and_refs();
bool _handle_val_anchors_and_refs();
void _move_val_tag_to_key_tag();
void _move_key_tag_to_val_tag();
void _move_key_tag2_to_key_tag();
void _move_val_anchor_to_key_anchor();
void _move_key_anchor_to_val_anchor();
void _push_level(bool explicit_flow_chars = false);
void _pop_level();
void _start_unk(bool as_child=true);
void _start_map(bool as_child=true);
void _start_map_unk(bool as_child);
void _stop_map();
void _start_seq(bool as_child=true);
void _stop_seq();
void _start_seqimap();
void _stop_seqimap();
void _start_doc(bool as_child=true);
void _stop_doc();
void _start_new_doc(csubstr rem);
void _end_stream();
NodeData* _append_val(csubstr val, flag_t quoted=false);
NodeData* _append_key_val(csubstr val, flag_t val_quoted=false);
bool _rval_dash_start_or_continue_seq();
void _store_scalar(csubstr s, flag_t is_quoted);
csubstr _consume_scalar();
void _move_scalar_from_top();
inline NodeData* _append_val_null(const char *str) { _RYML_CB_ASSERT(m_stack.m_callbacks, str >= m_buf.begin() && str <= m_buf.end()); (void)str; return _append_val({nullptr, size_t(0)}); }
inline NodeData* _append_key_val_null(const char *str) { _RYML_CB_ASSERT(m_stack.m_callbacks, str >= m_buf.begin() && str <= m_buf.end()); (void)str; return _append_key_val({nullptr, size_t(0)}); }
inline void _store_scalar_null(const char *str) { _RYML_CB_ASSERT(m_stack.m_callbacks, str >= m_buf.begin() && str <= m_buf.end()); (void)str; _store_scalar({nullptr, size_t(0)}, false); }
void _set_indentation(size_t behind);
void _save_indentation(size_t behind=0);
bool _maybe_set_indentation_from_anchor_or_tag();
void _write_key_anchor(size_t node_id);
void _write_val_anchor(size_t node_id);
void _handle_directive(csubstr directive);
void _skipchars(char c);
template<size_t N>
void _skipchars(const char (&chars)[N]);
private:
static size_t _count_nlines(csubstr src);
private:
typedef enum : flag_t {
RTOP = 0x01 << 0, ///< reading at top level
RUNK = 0x01 << 1, ///< reading an unknown: must determine whether scalar, map or seq
RMAP = 0x01 << 2, ///< reading a map
RSEQ = 0x01 << 3, ///< reading a seq
FLOW = 0x01 << 4, ///< reading is inside explicit flow chars: [] or {}
QMRK = 0x01 << 5, ///< reading an explicit key (`? key`)
RKEY = 0x01 << 6, ///< reading a scalar as key
RVAL = 0x01 << 7, ///< reading a scalar as val
RNXT = 0x01 << 8, ///< read next val or keyval
SSCL = 0x01 << 9, ///< there's a stored scalar
QSCL = 0x01 << 10, ///< stored scalar was quoted
RSET = 0x01 << 11, ///< the (implicit) map being read is a !!set. @see https://yaml.org/type/set.html
NDOC = 0x01 << 12, ///< no document mode. a document has ended and another has not started yet.
//! reading an implicit map nested in an explicit seq.
//! eg, {key: [key2: value2, key3: value3]}
//! is parsed as {key: [{key2: value2}, {key3: value3}]}
RSEQIMAP = 0x01 << 13,
} State_e;
struct LineContents
{
csubstr full; ///< the full line, including newlines on the right
csubstr stripped; ///< the stripped line, excluding newlines on the right
csubstr rem; ///< the stripped line remainder; initially starts at the first non-space character
size_t indentation; ///< the number of spaces on the beginning of the line
LineContents() : full(), stripped(), rem(), indentation() {}
void reset_with_next_line(csubstr buf, size_t pos);
void reset(csubstr full_, csubstr stripped_)
{
full = full_;
stripped = stripped_;
rem = stripped_;
// find the first column where the character is not a space
indentation = full.first_not_of(' ');
}
size_t current_col() const
{
return current_col(rem);
}
size_t current_col(csubstr s) const
{
RYML_ASSERT(s.str >= full.str);
RYML_ASSERT(full.is_super(s));
size_t col = static_cast<size_t>(s.str - full.str);
return col;
}
};
struct State
{
flag_t flags;
size_t level;
size_t node_id; // don't hold a pointer to the node as it will be relocated during tree resizes
csubstr scalar;
size_t scalar_col; // the column where the scalar (or its quotes) begin
Location pos;
LineContents line_contents;
size_t indref;
State() : flags(), level(), node_id(), scalar(), scalar_col(), pos(), line_contents(), indref() {}
void reset(const char *file, size_t node_id_)
{
flags = RUNK|RTOP;
level = 0;
pos.name = to_csubstr(file);
pos.offset = 0;
pos.line = 1;
pos.col = 1;
node_id = node_id_;
scalar_col = 0;
scalar.clear();
indref = 0;
}
};
void _line_progressed(size_t ahead);
void _line_ended();
void _line_ended_undo();
void _prepare_pop()
{
RYML_ASSERT(m_stack.size() > 1);
State const& curr = m_stack.top();
State & next = m_stack.top(1);
next.pos = curr.pos;
next.line_contents = curr.line_contents;
next.scalar = curr.scalar;
}
inline bool _at_line_begin() const
{
return m_state->line_contents.rem.begin() == m_state->line_contents.full.begin();
}
inline bool _at_line_end() const
{
csubstr r = m_state->line_contents.rem;
return r.empty() || r.begins_with(' ', r.len);
}
inline bool _token_is_from_this_line(csubstr token) const
{
return token.is_sub(m_state->line_contents.full);
}
inline NodeData * node(State const* s) const { return m_tree->get(s->node_id); }
inline NodeData * node(State const& s) const { return m_tree->get(s .node_id); }
inline NodeData * node(size_t node_id) const { return m_tree->get( node_id); }
inline bool has_all(flag_t f) const { return (m_state->flags & f) == f; }
inline bool has_any(flag_t f) const { return (m_state->flags & f) != 0; }
inline bool has_none(flag_t f) const { return (m_state->flags & f) == 0; }
static inline bool has_all(flag_t f, State const* s) { return (s->flags & f) == f; }
static inline bool has_any(flag_t f, State const* s) { return (s->flags & f) != 0; }
static inline bool has_none(flag_t f, State const* s) { return (s->flags & f) == 0; }
inline void set_flags(flag_t f) { set_flags(f, m_state); }
inline void add_flags(flag_t on) { add_flags(on, m_state); }
inline void addrem_flags(flag_t on, flag_t off) { addrem_flags(on, off, m_state); }
inline void rem_flags(flag_t off) { rem_flags(off, m_state); }
void set_flags(flag_t f, State * s);
void add_flags(flag_t on, State * s);
void addrem_flags(flag_t on, flag_t off, State * s);
void rem_flags(flag_t off, State * s);
void _resize_filter_arena(size_t num_characters);
void _grow_filter_arena(size_t num_characters);
substr _finish_filter_arena(substr dst, size_t pos);
void _prepare_locations();
void _resize_locations(size_t sz);
bool _locations_dirty() const;
bool _location_from_cont(Tree const& tree, size_t node, Location *C4_RESTRICT loc) const;
bool _location_from_node(Tree const& tree, size_t node, Location *C4_RESTRICT loc, size_t level) const;
private:
void _free();
void _clr();
void _cp(Parser const* that);
void _mv(Parser *that);
#ifdef RYML_DBG
template<class ...Args> void _dbg(csubstr fmt, Args const& C4_RESTRICT ...args) const;
#endif
template<class ...Args> [[noreturn]] void _err(csubstr fmt, Args const& C4_RESTRICT ...args) const;
template<class DumpFn> void _fmt_msg(DumpFn &&dumpfn) const;
static csubstr _prfl(substr buf, flag_t v);
private:
ParserOptions m_options;
csubstr m_file;
substr m_buf;
size_t m_root_id;
Tree * m_tree;
detail::stack<State> m_stack;
State * m_state;
size_t m_key_tag_indentation;
size_t m_key_tag2_indentation;
csubstr m_key_tag;
csubstr m_key_tag2;
size_t m_val_tag_indentation;
csubstr m_val_tag;
bool m_key_anchor_was_before;
size_t m_key_anchor_indentation;
csubstr m_key_anchor;
size_t m_val_anchor_indentation;
csubstr m_val_anchor;
substr m_filter_arena;
size_t *m_newline_offsets;
size_t m_newline_offsets_size;
size_t m_newline_offsets_capacity;
csubstr m_newline_offsets_buf;
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** @defgroup doc_parse_in_place Parse in place
*
* Parse a mutable YAML source buffer to create a ryml @ref Tree,
* potentially mutating the buffer.
*
* These freestanding functions use a temporary parser object, and are
* convenience functions to easily parse YAML without the need to
* instantiate a separate parser. Note that some properties (notably
* node locations in the original source code) are only available
* through the parser object after it has parsed the code. If you need
* access to any of these properties, use Parser::parse_in_place().
*
* @see Parser */
/** @{ */
inline Tree parse_in_place( substr yaml ) { Parser np; return np.parse_in_place({} , yaml); } //!< parse in-situ a modifiable YAML source buffer.
inline Tree parse_in_place(csubstr filename, substr yaml ) { Parser np; return np.parse_in_place(filename, yaml); } //!< parse in-situ a modifiable YAML source buffer, providing a filename for error messages.
inline void parse_in_place( substr yaml, Tree *t ) { Parser np; np.parse_in_place({} , yaml, t); } //!< reusing the YAML tree, parse in-situ a modifiable YAML source buffer
inline void parse_in_place(csubstr filename, substr yaml, Tree *t ) { Parser np; np.parse_in_place(filename, yaml, t); } //!< reusing the YAML tree, parse in-situ a modifiable YAML source buffer, providing a filename for error messages.
inline void parse_in_place( substr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_place({} , yaml, t, node_id); } //!< reusing the YAML tree, parse in-situ a modifiable YAML source buffer
inline void parse_in_place(csubstr filename, substr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_place(filename, yaml, t, node_id); } //!< reusing the YAML tree, parse in-situ a modifiable YAML source buffer, providing a filename for error messages.
inline void parse_in_place( substr yaml, NodeRef node ) { Parser np; np.parse_in_place({} , yaml, node); } //!< reusing the YAML tree, parse in-situ a modifiable YAML source buffer
inline void parse_in_place(csubstr filename, substr yaml, NodeRef node ) { Parser np; np.parse_in_place(filename, yaml, node); } //!< reusing the YAML tree, parse in-situ a modifiable YAML source buffer, providing a filename for error messages.
/** @cond dev */
RYML_DEPRECATED("use parse_in_place() instead") inline Tree parse( substr yaml ) { Parser np; return np.parse_in_place({} , yaml); }
RYML_DEPRECATED("use parse_in_place() instead") inline Tree parse(csubstr filename, substr yaml ) { Parser np; return np.parse_in_place(filename, yaml); }
RYML_DEPRECATED("use parse_in_place() instead") inline void parse( substr yaml, Tree *t ) { Parser np; np.parse_in_place({} , yaml, t); }
RYML_DEPRECATED("use parse_in_place() instead") inline void parse(csubstr filename, substr yaml, Tree *t ) { Parser np; np.parse_in_place(filename, yaml, t); }
RYML_DEPRECATED("use parse_in_place() instead") inline void parse( substr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_place({} , yaml, t, node_id); }
RYML_DEPRECATED("use parse_in_place() instead") inline void parse(csubstr filename, substr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_place(filename, yaml, t, node_id); }
RYML_DEPRECATED("use parse_in_place() instead") inline void parse( substr yaml, NodeRef node ) { Parser np; np.parse_in_place({} , yaml, node); }
RYML_DEPRECATED("use parse_in_place() instead") inline void parse(csubstr filename, substr yaml, NodeRef node ) { Parser np; np.parse_in_place(filename, yaml, node); }
/** @endcond */
/** @} */
//-----------------------------------------------------------------------------
/** @defgroup doc_parse_in_arena Parse in arena
*
* Parse a read-only YAML source buffer to create a ryml @ref Tree,
* copying the buffer first to the tree's arena. The copy of the
* buffer is then parsed in place.
*
* These freestanding functions use a temporary parser object, and are
* convenience functions to easily parse YAML without the need to
* instantiate a separate parser. Note that some properties (notably
* node locations in the original source code) are only available
* through the parser object after it has parsed the code. If you need
* access to any of these properties, use Parser::parse_in_arena().
*
* @note overloads receiving a substr YAML buffer are intentionally
* left undefined, such that calling parse_in_arena() with a substr
* will cause a linker error. This is to prevent an accidental
* copy of the source buffer to the tree's arena, because substr
* is implicitly convertible to csubstr. If you really intend to parse
* a mutable buffer in the tree's arena, convert it first to immutable
* by assigning the substr to a csubstr prior to calling parse_in_arena().
* This is not needed for parse_in_place() because csubstr is not
* implicitly convertible to substr.
*
* @see Parser */
/** @{ */
/* READ THE NOTE ABOVE! */
/** @cond dev */
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) Tree parse_in_arena( substr yaml );
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) Tree parse_in_arena(csubstr filename, substr yaml );
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena( substr yaml, Tree *t );
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr yaml, Tree *t );
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena( substr yaml, Tree *t, size_t node_id);
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr yaml, Tree *t, size_t node_id);
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena( substr yaml, NodeRef node );
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr yaml, NodeRef node );
/** @endcond */
inline Tree parse_in_arena( csubstr yaml ) { Parser np; return np.parse_in_arena({} , yaml); } //!< parse a read-only YAML source buffer, copying it first to the tree's source arena.
inline Tree parse_in_arena(csubstr filename, csubstr yaml ) { Parser np; return np.parse_in_arena(filename, yaml); } //!< parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
inline void parse_in_arena( csubstr yaml, Tree *t ) { Parser np; np.parse_in_arena({} , yaml, t); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena.
inline void parse_in_arena(csubstr filename, csubstr yaml, Tree *t ) { Parser np; np.parse_in_arena(filename, yaml, t); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
inline void parse_in_arena( csubstr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_arena({} , yaml, t, node_id); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena.
inline void parse_in_arena(csubstr filename, csubstr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_arena(filename, yaml, t, node_id); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
inline void parse_in_arena( csubstr yaml, NodeRef node ) { Parser np; np.parse_in_arena({} , yaml, node); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena.
inline void parse_in_arena(csubstr filename, csubstr yaml, NodeRef node ) { Parser np; np.parse_in_arena(filename, yaml, node); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
/** @cond dev */
RYML_DEPRECATED("use parse_in_arena() instead") inline Tree parse( csubstr yaml ) { Parser np; return np.parse_in_arena({} , yaml); } //!< parse a read-only YAML source buffer, copying it first to the tree's source arena.
RYML_DEPRECATED("use parse_in_arena() instead") inline Tree parse(csubstr filename, csubstr yaml ) { Parser np; return np.parse_in_arena(filename, yaml); } //!< parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
RYML_DEPRECATED("use parse_in_arena() instead") inline void parse( csubstr yaml, Tree *t ) { Parser np; np.parse_in_arena({} , yaml, t); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena.
RYML_DEPRECATED("use parse_in_arena() instead") inline void parse(csubstr filename, csubstr yaml, Tree *t ) { Parser np; np.parse_in_arena(filename, yaml, t); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
RYML_DEPRECATED("use parse_in_arena() instead") inline void parse( csubstr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_arena({} , yaml, t, node_id); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena.
RYML_DEPRECATED("use parse_in_arena() instead") inline void parse(csubstr filename, csubstr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_arena(filename, yaml, t, node_id); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
RYML_DEPRECATED("use parse_in_arena() instead") inline void parse( csubstr yaml, NodeRef node ) { Parser np; np.parse_in_arena({} , yaml, node); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena.
RYML_DEPRECATED("use parse_in_arena() instead") inline void parse(csubstr filename, csubstr yaml, NodeRef node ) { Parser np; np.parse_in_arena(filename, yaml, node); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
/** @endcond */
/** @} */
/** @} */
} // namespace yml
} // namespace c4
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
#endif /* _C4_YML_PARSE_HPP_ */

View File

@ -0,0 +1,97 @@
#ifndef _C4_YML_PREPROCESS_HPP_
#define _C4_YML_PREPROCESS_HPP_
/** @file preprocess.hpp Functions for preprocessing YAML prior to parsing. */
#ifndef _C4_YML_COMMON_HPP_
#include "./common.hpp"
#endif
#include <c4/substr.hpp>
namespace c4 {
namespace yml {
/** @addtogroup doc_preprocessors
* @{
*/
/** @cond dev */
namespace detail {
using Preprocessor = size_t(csubstr, substr);
template<Preprocessor PP, class CharContainer>
substr preprocess_into_container(csubstr input, CharContainer *out)
{
// try to write once. the preprocessor will stop writing at the end of
// the container, but will process all the input to determine the
// required container size.
size_t sz = PP(input, to_substr(*out));
// if the container size is not enough, resize, and run again in the
// resized container
if(sz > out->size())
{
out->resize(sz);
sz = PP(input, to_substr(*out));
}
return to_substr(*out).first(sz);
}
} // namespace detail
/** @endcond */
//-----------------------------------------------------------------------------
/** @defgroup doc_preprocess_rxmap preprocess_rxmap
*
* @brief Convert flow-type relaxed maps (with implicit bools) into strict YAML
* flow map:
*
* @code{.yaml}
* {a, b, c, d: [e, f], g: {a, b}}
* # is converted into this:
* {a: 1, b: 1, c: 1, d: [e, f], g: {a, b}}
* @endcode
* @note this is NOT recursive - conversion happens only in the top-level map
* @param rxmap A relaxed map
* @param buf output buffer
* @param out output container
*
* @{
*/
/** Write into a given output buffer. This function is safe to call with
* empty or small buffers; it won't write beyond the end of the buffer.
*
* @return the number of characters required for output
*/
RYML_EXPORT size_t preprocess_rxmap(csubstr rxmap, substr buf);
/** Write into an existing container. It is resized to contained the output.
* @return a substr of the container
* @overload preprocess_rxmap */
template<class CharContainer>
substr preprocess_rxmap(csubstr rxmap, CharContainer *out)
{
return detail::preprocess_into_container<preprocess_rxmap>(rxmap, out);
}
/** Create a container with the result.
* @overload preprocess_rxmap */
template<class CharContainer>
CharContainer preprocess_rxmap(csubstr rxmap)
{
CharContainer out;
preprocess_rxmap(rxmap, &out);
return out;
}
/** @} */ // preprocess_rxmap
/** @} */ // group
} // namespace yml
} // namespace c4
#endif /* _C4_YML_PREPROCESS_HPP_ */

View File

@ -0,0 +1,45 @@
#ifndef _C4_YML_STD_MAP_HPP_
#define _C4_YML_STD_MAP_HPP_
/** @file map.hpp write/read std::map to/from a YAML tree. */
#include "c4/yml/node.hpp"
#include <map>
namespace c4 {
namespace yml {
// std::map requires child nodes in the data
// tree hierarchy (a MAP node in ryml parlance).
// So it should be serialized via write()/read().
template<class K, class V, class Less, class Alloc>
void write(c4::yml::NodeRef *n, std::map<K, V, Less, Alloc> const& m)
{
*n |= c4::yml::MAP;
for(auto const& C4_RESTRICT p : m)
{
auto ch = n->append_child();
ch << c4::yml::key(p.first);
ch << p.second;
}
}
template<class K, class V, class Less, class Alloc>
bool read(c4::yml::ConstNodeRef const& n, std::map<K, V, Less, Alloc> * m)
{
K k{};
V v{};
for(auto const& C4_RESTRICT ch : n)
{
ch >> c4::yml::key(k);
ch >> v;
m->emplace(std::make_pair(std::move(k), std::move(v)));
}
return true;
}
} // namespace yml
} // namespace c4
#endif // _C4_YML_STD_MAP_HPP_

View File

@ -0,0 +1,8 @@
#ifndef _C4_YML_STD_STD_HPP_
#define _C4_YML_STD_STD_HPP_
#include "c4/yml/std/string.hpp"
#include "c4/yml/std/vector.hpp"
#include "c4/yml/std/map.hpp"
#endif // _C4_YML_STD_STD_HPP_

View File

@ -0,0 +1,9 @@
#ifndef C4_YML_STD_STRING_HPP_
#define C4_YML_STD_STRING_HPP_
/** @file string.hpp substring conversions for/from std::string */
// everything we need is implemented here:
#include <c4/std/string.hpp>
#endif // C4_YML_STD_STRING_HPP_

View File

@ -0,0 +1,53 @@
#ifndef _C4_YML_STD_VECTOR_HPP_
#define _C4_YML_STD_VECTOR_HPP_
#include "c4/yml/node.hpp"
#include <c4/std/vector.hpp>
#include <vector>
namespace c4 {
namespace yml {
// vector is a sequence-like type, and it requires child nodes
// in the data tree hierarchy (a SEQ node in ryml parlance).
// So it should be serialized via write()/read().
template<class V, class Alloc>
void write(c4::yml::NodeRef *n, std::vector<V, Alloc> const& vec)
{
*n |= c4::yml::SEQ;
for(auto const& v : vec)
n->append_child() << v;
}
template<class V, class Alloc>
bool read(c4::yml::ConstNodeRef const& n, std::vector<V, Alloc> *vec)
{
vec->resize(n.num_children());
size_t pos = 0;
for(auto const ch : n)
ch >> (*vec)[pos++];
return true;
}
/** specialization: std::vector<bool> uses std::vector<bool>::reference as
* the return value of its operator[]. */
template<class Alloc>
bool read(c4::yml::ConstNodeRef const& n, std::vector<bool, Alloc> *vec)
{
vec->resize(n.num_children());
size_t pos = 0;
bool tmp = false;
for(auto const ch : n)
{
ch >> tmp;
(*vec)[pos++] = tmp;
}
return true;
}
} // namespace yml
} // namespace c4
#endif // _C4_YML_STD_VECTOR_HPP_

1523
3rdparty/rapidyaml/include/c4/yml/tree.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,242 @@
#ifndef _C4_YML_WRITER_HPP_
#define _C4_YML_WRITER_HPP_
#ifndef _C4_YML_COMMON_HPP_
#include "./common.hpp"
#endif
#include <c4/substr.hpp>
#include <stdio.h> // fwrite(), fputc()
#include <string.h> // memcpy()
namespace c4 {
namespace yml {
/** @addtogroup doc_emit
* @{
*/
/** @defgroup doc_writers Writer objects to use with an Emitter
* @see Emitter
* @{
*/
/** Repeat-Character: a character to be written a number of times. */
struct RepC
{
char c;
size_t num_times;
};
inline RepC indent_to(size_t num_levels)
{
return {' ', size_t(2) * num_levels};
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** A writer that outputs to a file. Defaults to stdout. */
struct WriterFile
{
FILE * m_file;
size_t m_pos;
WriterFile(FILE *f = nullptr) : m_file(f ? f : stdout), m_pos(0) {}
inline substr _get(bool /*error_on_excess*/)
{
substr sp;
sp.str = nullptr;
sp.len = m_pos;
return sp;
}
template<size_t N>
inline void _do_write(const char (&a)[N])
{
fwrite(a, sizeof(char), N - 1, m_file);
m_pos += N - 1;
}
inline void _do_write(csubstr sp)
{
#if defined(__clang__)
# pragma clang diagnostic push
# pragma GCC diagnostic ignored "-Wsign-conversion"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
if(sp.empty()) return;
fwrite(sp.str, sizeof(csubstr::char_type), sp.len, m_file);
m_pos += sp.len;
#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
}
inline void _do_write(const char c)
{
fputc(c, m_file);
++m_pos;
}
inline void _do_write(RepC const rc)
{
for(size_t i = 0; i < rc.num_times; ++i)
{
fputc(rc.c, m_file);
}
m_pos += rc.num_times;
}
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** A writer that outputs to an STL-like ostream. */
template<class OStream>
struct WriterOStream
{
OStream& m_stream;
size_t m_pos;
WriterOStream(OStream &s) : m_stream(s), m_pos(0) {}
inline substr _get(bool /*error_on_excess*/)
{
substr sp;
sp.str = nullptr;
sp.len = m_pos;
return sp;
}
template<size_t N>
inline void _do_write(const char (&a)[N])
{
m_stream.write(a, N - 1);
m_pos += N - 1;
}
inline void _do_write(csubstr sp)
{
#if defined(__clang__)
# pragma clang diagnostic push
# pragma GCC diagnostic ignored "-Wsign-conversion"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
if(sp.empty()) return;
m_stream.write(sp.str, sp.len);
m_pos += sp.len;
#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
}
inline void _do_write(const char c)
{
m_stream.put(c);
++m_pos;
}
inline void _do_write(RepC const rc)
{
for(size_t i = 0; i < rc.num_times; ++i)
{
m_stream.put(rc.c);
}
m_pos += rc.num_times;
}
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** a writer to a substr */
struct WriterBuf
{
substr m_buf;
size_t m_pos;
WriterBuf(substr sp) : m_buf(sp), m_pos(0) {}
inline substr _get(bool error_on_excess)
{
if(m_pos <= m_buf.len)
{
return m_buf.first(m_pos);
}
if(error_on_excess)
{
c4::yml::error("not enough space in the given buffer");
}
substr sp;
sp.str = nullptr;
sp.len = m_pos;
return sp;
}
template<size_t N>
inline void _do_write(const char (&a)[N])
{
RYML_ASSERT( ! m_buf.overlaps(a));
if(m_pos + N-1 <= m_buf.len)
{
memcpy(&(m_buf[m_pos]), a, N-1);
}
m_pos += N-1;
}
inline void _do_write(csubstr sp)
{
if(sp.empty()) return;
RYML_ASSERT( ! sp.overlaps(m_buf));
if(m_pos + sp.len <= m_buf.len)
{
memcpy(&(m_buf[m_pos]), sp.str, sp.len);
}
m_pos += sp.len;
}
inline void _do_write(const char c)
{
if(m_pos + 1 <= m_buf.len)
{
m_buf[m_pos] = c;
}
++m_pos;
}
inline void _do_write(RepC const rc)
{
if(m_pos + rc.num_times <= m_buf.len)
{
for(size_t i = 0; i < rc.num_times; ++i)
{
m_buf[m_pos + i] = rc.c;
}
}
m_pos += rc.num_times;
}
};
/** @ } */
/** @ } */
} // namespace yml
} // namespace c4
#endif /* _C4_YML_WRITER_HPP_ */

View File

@ -0,0 +1,10 @@
#ifndef _C4_YML_YML_HPP_
#define _C4_YML_YML_HPP_
#include "c4/yml/tree.hpp"
#include "c4/yml/node.hpp"
#include "c4/yml/emit.hpp"
#include "c4/yml/parse.hpp"
#include "c4/yml/preprocess.hpp"
#endif // _C4_YML_YML_HPP_

View File

@ -0,0 +1,391 @@
# To make this file known to Qt Creator using:
# Tools > Options > Debugger > Locals & Expressions > Extra Debugging Helpers
# Any contents here will be picked up by GDB, LLDB, and CDB based
# debugging in Qt Creator automatically.
# Example to display a simple type
# template<typename U, typename V> struct MapNode
# {
# U key;
# V data;
# }
#
# def qdump__MapNode(d, value):
# d.putValue("This is the value column contents")
# d.putExpandable()
# if d.isExpanded():
# with Children(d):
# # Compact simple case.
# d.putSubItem("key", value["key"])
# # Same effect, with more customization possibilities.
# with SubItem(d, "data")
# d.putItem("data", value["data"])
# Check http://doc.qt.io/qtcreator/creator-debugging-helpers.html
# for more details or look at qttypes.py, stdtypes.py, boosttypes.py
# for more complex examples.
# to try parsing:
# env PYTHONPATH=/usr/share/qtcreator/debugger/ python src/ryml-gdbtypes.py
import dumper
#from dumper import Dumper, Value, Children, SubItem
#from dumper import SubItem, Children
from dumper import *
import sys
import os
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# QtCreator makes it really hard to figure out problems in this code.
# So here are some debugging utilities.
# FIXME. this decorator is not working; find out why.
def dbglog(func):
"""a decorator that logs entry and exit of functions"""
if not _DBG:
return func
def func_wrapper(*args, **kwargs):
_dbg_enter(func.__name__)
ret = func(*args, **kwargs)
_dbg_exit(func.__name__)
return ret
return func_wrapper
_DBG = False
_dbg_log = None
_dbg_stack = 0
def _dbg(*args, **kwargs):
global _dbg_log, _dbg_stack
if not _DBG:
return
if _dbg_log is None:
filename = os.path.join(os.path.dirname(__file__), "dbg.txt")
_dbg_log = open(filename, "w")
kwargs['file'] = _dbg_log
kwargs['flush'] = True
print(" " * _dbg_stack, *args, **kwargs)
def _dbg_enter(name):
global _dbg_stack
_dbg(name, "- enter")
_dbg_stack += 1
def _dbg_exit(name):
global _dbg_stack
_dbg_stack -= 1
_dbg(name, "- exit!")
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
NPOS = 18446744073709551615
MAX_SUBSTR_LEN_DISPLAY = 80
MAX_SUBSTR_LEN_EXPAND = 1000
def get_str_value(d, value, limit=0):
# adapted from dumper.py::Dumper::putCharArrayValue()
m_str = value["str"].pointer()
m_len = value["len"].integer()
if m_len == NPOS:
_dbg("getstr... 1", m_len)
m_str = "!!!!!<npos>!!!!!"
m_len = len(m_str)
return m_str, m_len
if limit == 0:
limit = d.displayStringLimit
elided, shown = d.computeLimit(m_len, limit)
mem = bytes(d.readRawMemory(m_str, shown))
mem = mem.decode('utf8')
return mem, m_len
def __display_csubstr(d, value, limit=0):
m_str, m_len = get_str_value(d, value)
safe_len = min(m_len, MAX_SUBSTR_LEN_DISPLAY)
disp = m_str[0:safe_len]
# ensure the string escapes characters like \n\r\t etc
disp = disp.encode('unicode_escape').decode('utf8')
# WATCHOUT. quotes in the string will make qtcreator hang!!!
disp = disp.replace('"', '\\"')
disp = disp.replace('\'', '\\')
if m_len <= MAX_SUBSTR_LEN_DISPLAY:
d.putValue(f"[{m_len}] '{disp}'")
else:
d.putValue(f"[{m_len}] '{disp}'...")
return m_str, m_len
def qdump__c4__csubstr(d, value):
m_str, m_len = __display_csubstr(d, value)
d.putExpandable()
if d.isExpanded():
with Children(d):
safe_len = min(m_len, MAX_SUBSTR_LEN_EXPAND)
for i in range(safe_len):
ct = d.createType('char')
d.putSubItem(safe_len, d.createValue(value["str"].pointer() + i, ct))
d.putSubItem("len", value["len"])
d.putPtrItem("str", value["str"].pointer())
def qdump__c4__substr(d, value):
qdump__c4__csubstr(d, value)
def qdump__c4__basic_substring(d, value):
qdump__c4__csubstr(d, value)
def qdump__c4__yml__NodeScalar(d, value):
alen = value["anchor"]["len"].integer()
tlen = value["tag" ]["len"].integer()
m_str, m_len = get_str_value(d, value["scalar"])
if alen == 0 and tlen == 0:
d.putValue(f'\'{m_str}\'')
elif alen == 0 and tlen > 0:
d.putValue(f'\'{m_str}\' [Ta]')
elif alen > 0 and tlen == 0:
d.putValue(f'\'{m_str}\' [tA]')
elif alen > 0 and tlen > 0:
d.putValue(f'\'{m_str}\' [TA]')
d.putExpandable()
if d.isExpanded():
with Children(d):
d.putSubItem("[scalar]", value["scalar"])
if tlen > 0:
d.putSubItem("[tag]", value["tag"])
if alen > 0:
d.putSubItem("[anchor or ref]", value["anchor"])
def _format_enum_value(int_value, enum_map):
str_value = enum_map.get(int_value, None)
display = f'{int_value}' if str_value is None else f'{str_value} ({int_value})'
return display
def _format_bitmask_value(int_value, enum_map):
str_value = enum_map.get(int_value, None)
if str_value:
return f'{str_value} ({int_value})'
else:
out = ""
orig = int_value
# do in reverse to get compound flags first
for k, v in reversed(enum_map.items()):
if (k != 0):
if (int_value & k) == k:
if len(out) > 0:
out += '|'
out += v
int_value &= ~k
else:
if len(out) == 0 and int_value == 0:
return v
if out == "":
return f'{int_value}'
return f"{out} ({orig})"
def _c4bit(*ints):
ret = 0
for i in ints:
ret |= 1 << i
return ret
node_types = {
0: "NOTYPE",
_c4bit(0): "VAL" ,
_c4bit(1): "KEY" ,
_c4bit(2): "MAP" ,
_c4bit(3): "SEQ" ,
_c4bit(4): "DOC" ,
_c4bit(5,3): "STREAM",
_c4bit(6): "KEYREF" ,
_c4bit(7): "VALREF" ,
_c4bit(8): "KEYANCH" ,
_c4bit(9): "VALANCH" ,
_c4bit(10): "KEYTAG" ,
_c4bit(11): "VALTAG" ,
_c4bit(12): "VALQUO" ,
_c4bit(13): "KEYQUO" ,
_c4bit(1,0): "KEYVAL",
_c4bit(1,3): "KEYSEQ",
_c4bit(1,2): "KEYMAP",
_c4bit(4,2): "DOCMAP",
_c4bit(4,3): "DOCSEQ",
_c4bit(4,0): "DOCVAL",
#
_c4bit(14): "STYLE_FLOW_SL",
_c4bit(15): "STYLE_FLOW_ML",
_c4bit(16): "STYLE_BLOCK",
#
_c4bit(17): "KEY_LITERAL",
_c4bit(18): "VAL_LITERAL",
_c4bit(19): "KEY_FOLDED",
_c4bit(20): "VAL_FOLDED",
_c4bit(21): "KEY_SQUO",
_c4bit(22): "VAL_SQUO",
_c4bit(23): "KEY_DQUO",
_c4bit(24): "VAL_DQUO",
_c4bit(25): "KEY_PLAIN",
_c4bit(26): "VAL_PLAIN",
}
node_types_rev = {v: k for k, v in node_types.items()}
def _node_type_has_all(node_type_value, type_name):
exp = node_types_rev[type_name]
return (node_type_value & exp) == exp
def _node_type_has_any(node_type_value, type_name):
exp = node_types_rev[type_name]
return (node_type_value & exp) != 0
def qdump__c4__yml__NodeType_e(d, value):
v = _format_bitmask_value(value.integer(), node_types)
d.putValue(v)
def qdump__c4__yml__NodeType(d, value):
qdump__c4__yml__NodeType_e(d, value["type"])
def qdump__c4__yml__NodeData(d, value):
d.putValue("wtf")
ty = _format_bitmask_value(value.integer(), node_types)
t = value["m_type"]["type"].integer()
k = value["m_key"]["scalar"]
v = value["m_val"]["scalar"]
sk, lk = get_str_value(d, k)
sv, lv = get_str_value(d, v)
if _node_type_has_all(t, "KEYVAL"):
d.putValue(f"'{sk}': '{sv}' {ty}")
elif _node_type_has_any(t, "KEY"):
d.putValue(f"'{sk}': {ty}")
elif _node_type_has_any(t, "VAL"):
d.putValue(f"'{sv}' {ty}")
else:
d.putValue(f"{ty}")
d.putExpandable()
if d.isExpanded():
with Children(d):
d.putSubItem("m_type", value["m_type"])
# key
if _node_type_has_any(t, "KEY"):
d.putSubItem("m_key", value["m_key"])
if _node_type_has_any(t, "KEYREF"):
with SubItem(d, "m_key.ref"):
s_, _ = get_str_value(d, value["m_key"]["anchor"])
d.putValue(f"'{s_}'")
if _node_type_has_any(t, "KEYANCH"):
with SubItem(d, "m_key.anchor"):
s_, _ = get_str_value(d, value["m_key"]["anchor"])
d.putValue(f"'{s_}'")
if _node_type_has_any(t, "KEYTAG"):
with SubItem(d, "m_key.tag"):
s_, _ = get_str_value(d, value["m_key"]["tag"])
d.putValue(f"'{s_}'")
# val
if _node_type_has_any(t, "VAL"):
d.putSubItem("m_val", value["m_val"])
if _node_type_has_any(t, "VALREF"):
with SubItem(d, "m_val.ref"):
s_, _ = get_str_value(d, value["m_val"]["anchor"])
d.putValue(f"'{s_}'")
if _node_type_has_any(t, "VALANCH"):
with SubItem(d, "m_val.anchor"):
s_, _ = get_str_value(d, value["m_val"]["anchor"])
d.putValue(f"'{s_}'")
if _node_type_has_any(t, "VALTAG"):
with SubItem(d, "m_val.tag"):
s_, _ = get_str_value(d, value["m_val"]["tag"])
d.putValue(f"'{s_}'")
# hierarchy
_dump_node_index(d, "m_parent", value)
_dump_node_index(d, "m_first_child", value)
_dump_node_index(d, "m_last_child", value)
_dump_node_index(d, "m_next_sibling", value)
_dump_node_index(d, "m_prev_sibling", value)
def _dump_node_index(d, name, value):
if int(value[name].integer()) == NPOS:
pass
#with SubItem(d, name):
# d.putValue("-")
else:
d.putSubItem(name, value[name])
# c4::yml::Tree
def qdump__c4__yml__Tree(d, value):
m_size = value["m_size"].integer()
m_cap = value["m_cap"].integer()
d.putExpandable()
if d.isExpanded():
#d.putArrayData(value["m_buf"], m_size, value["m_buf"].dereference())
with Children(d):
with SubItem(d, f"[nodes]"):
d.putItemCount(m_size)
d.putArrayData(value["m_buf"].pointer(), m_size, value["m_buf"].type.dereference())
d.putPtrItem("m_buf", value["m_buf"].pointer())
d.putIntItem("m_size", value["m_size"])
d.putIntItem("m_cap (capacity)", value["m_cap"])
d.putIntItem("[slack]", m_cap - m_size)
d.putIntItem("m_free_head", value["m_free_head"])
d.putIntItem("m_free_tail", value["m_free_tail"])
d.putSubItem("m_arena", value["m_arena"])
def qdump__c4__yml__detail__stack(d, value):
T = value.type[0]
N = value.type[0]
m_size = value["m_size"].integer()
m_capacity = value["m_capacity"].integer()
d.putItemCount(m_size)
if d.isExpanded():
with Children(d):
with SubItem(d, f"[nodes]"):
d.putItemCount(m_size)
d.putArrayData(value["m_stack"].pointer(), m_size, T)
d.putIntItem("m_size", value["m_size"])
d.putIntItem("m_capacity", value["m_capacity"])
#d.putIntItem("[small capacity]", N)
d.putIntItem("[is large]", value["m_buf"].address() == value["m_stack"].pointer())
d.putPtrItem("m_stack", value["m_stack"].pointer())
d.putPtrItem("m_buf", value["m_buf"].address())
def qdump__c4__yml__detail__ReferenceResolver__refdata(d, value):
node = value["node"].integer()
ty = _format_bitmask_value(value["type"].integer(), node_types)
d.putValue(f'{node} {ty}')
d.putExpandable()
if d.isExpanded():
with Children(d):
d.putSubItem("type", value["type"])
d.putSubItem("node", value["node"])
_dump_node_index(d, "prev_anchor", value)
_dump_node_index(d, "target", value)
_dump_node_index(d, "parent_ref", value)
_dump_node_index(d, "parent_ref_sibling", value)

11
3rdparty/rapidyaml/include/ryml.hpp vendored Normal file
View File

@ -0,0 +1,11 @@
#ifndef _RYML_HPP_
#define _RYML_HPP_
#include "c4/yml/yml.hpp"
namespace ryml {
using namespace c4::yml;
using namespace c4;
}
#endif /* _RYML_HPP_ */

219
3rdparty/rapidyaml/include/ryml.natvis vendored Normal file
View File

@ -0,0 +1,219 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Very good intro:
@see https://docs.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects?view=vs-2017
@see https://code.msdn.microsoft.com/windowsdesktop/Writing-type-visualizers-2eae77a2
See also:
@see http://blogs.msdn.com/b/vcblog/archive/2013/06/28/using-visual-studio-2013-to-write-maintainable-native-visualizations-natvis.aspx?PageIndex=2
@see http://blogs.msdn.com/b/vcblog/archive/2015/09/28/debug-visualizers-in-visual-c-2015.aspx
@see http://stackoverflow.com/questions/36883414/limit-display-of-char-in-natvis-file-to-specific-length
-->
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="c4::yml::NodeScalar">
<DisplayString Condition="(tag.len == 0) &amp;&amp; (anchor.len == 0)">{scalar.str,[scalar.len]}</DisplayString>
<DisplayString Condition="(tag.len > 0) &amp;&amp; (anchor.len == 0)">{scalar.str,[scalar.len]} [T]</DisplayString>
<DisplayString Condition="(tag.len == 0) &amp;&amp; (anchor.len > 0)">{scalar.str,[scalar.len]} [A]</DisplayString>
<DisplayString Condition="(tag.len > 0) &amp;&amp; (anchor.len > 0)">{scalar.str,[scalar.len]} [T][A]</DisplayString>
<Expand>
<Item Name="scalar">scalar</Item>
<Item Name="tag">tag</Item>
<Item Name="anchor">anchor</Item>
</Expand>
</Type>
<Type Name="c4::yml::NodeType">
<DisplayString>{type}</DisplayString>
<Expand>
<Synthetic Name="[enabled bits]">
<Expand>
<Item Name="[0]" Condition="(type &amp; c4::yml::VAL) != 0">c4::yml::VAL</Item>
<Item Name="[1]" Condition="(type &amp; c4::yml::KEY) != 0">c4::yml::KEY</Item>
<Item Name="[2]" Condition="(type &amp; c4::yml::MAP) != 0">c4::yml::MAP</Item>
<Item Name="[3]" Condition="(type &amp; c4::yml::SEQ) != 0">c4::yml::SEQ</Item>
<Item Name="[4]" Condition="(type &amp; c4::yml::DOC) != 0">c4::yml::DOC</Item>
<Item Name="[5]" Condition="(type &amp; c4::yml::STREAM) != 0">c4::yml::STREAM</Item>
<Item Name="[6]" Condition="(type &amp; c4::yml::KEYREF) != 0">c4::yml::KEYREF</Item>
<Item Name="[7]" Condition="(type &amp; c4::yml::VALREF) != 0">c4::yml::VALREF</Item>
<Item Name="[8]" Condition="(type &amp; c4::yml::KEYANCH) != 0">c4::yml::KEYANCH</Item>
<Item Name="[9]" Condition="(type &amp; c4::yml::VALANCH) != 0">c4::yml::VALANCH</Item>
<Item Name="[10]" Condition="(type &amp; c4::yml::KEYTAG) != 0">c4::yml::KEYTAG</Item>
<Item Name="[11]" Condition="(type &amp; c4::yml::VALTAG) != 0">c4::yml::VALTAG</Item>
<Item Name="[12]" Condition="(type &amp; c4::yml::VALQUO) != 0">c4::yml::VALQUO</Item>
<Item Name="[13]" Condition="(type &amp; c4::yml::KEYQUO) != 0">c4::yml::KEYQUO</Item>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="c4::yml::NodeData">
<DisplayString Condition="((m_type.type &amp; c4::yml::KEY ) == c4::yml::KEY) &amp;&amp; ((m_type.type &amp; c4::yml::VAL) == c4::yml::VAL)">[KEYVAL] {m_key.scalar.str,[m_key.scalar.len]}: {m_val.scalar.str,[m_val.scalar.len]}</DisplayString>
<DisplayString Condition="((m_type.type &amp; c4::yml::KEY ) == c4::yml::KEY) &amp;&amp; ((m_type.type &amp; c4::yml::SEQ) == c4::yml::SEQ)">[KEYSEQ] {m_key.scalar.str,[m_key.scalar.len]}</DisplayString>
<DisplayString Condition="((m_type.type &amp; c4::yml::KEY ) == c4::yml::KEY) &amp;&amp; ((m_type.type &amp; c4::yml::MAP) == c4::yml::MAP)">[KEYMAP] {m_key.scalar.str,[m_key.scalar.len]}</DisplayString>
<DisplayString Condition="((m_type.type &amp; c4::yml::DOC ) == c4::yml::DOC) &amp;&amp; ((m_type.type &amp; c4::yml::SEQ) == c4::yml::SEQ)">[DOCSEQ]</DisplayString>
<DisplayString Condition="((m_type.type &amp; c4::yml::DOC ) == c4::yml::DOC) &amp;&amp; ((m_type.type &amp; c4::yml::MAP) == c4::yml::MAP)">[DOCMAP]</DisplayString>
<DisplayString Condition="(m_type.type &amp; c4::yml::VAL ) == c4::yml::VAL" >[VAL] {m_val.scalar.str,[m_val.scalar.len]}</DisplayString>
<DisplayString Condition="(m_type.type &amp; c4::yml::KEY ) == c4::yml::KEY" >[KEY] {m_key.scalar.str,[m_key.scalar.len]}</DisplayString>
<DisplayString Condition="(m_type.type &amp; c4::yml::SEQ ) == c4::yml::SEQ" >[SEQ]</DisplayString>
<DisplayString Condition="(m_type.type &amp; c4::yml::MAP ) == c4::yml::MAP" >[MAP]</DisplayString>
<DisplayString Condition="(m_type.type &amp; c4::yml::DOC ) == c4::yml::DOC" >[DOC]</DisplayString>
<DisplayString Condition="(m_type.type &amp; c4::yml::STREAM) == c4::yml::STREAM">[STREAM]</DisplayString>
<DisplayString Condition="(m_type.type &amp; c4::yml::NOTYPE) == c4::yml::NOTYPE">[NOTYPE]</DisplayString>
<Expand>
<Item Name="type">m_type</Item>
<Item Name="key" Condition="(m_type.type &amp; c4::yml::KEY) != 0">m_key</Item>
<Item Name="val" Condition="(m_type.type &amp; c4::yml::VAL) != 0">m_val</Item>
<Item Name="key quoted" Condition="((m_type.type &amp; c4::yml::KEY) != 0) &amp;&amp; ((m_type.type &amp; c4::yml::KEYQUO) != 0)">c4::yml::KEYQUO</Item>
<Item Name="val quoted" Condition="((m_type.type &amp; c4::yml::VAL) != 0) &amp;&amp; ((m_type.type &amp; c4::yml::VALQUO) != 0)">c4::yml::VALQUO</Item>
<Item Name="key ref" Condition="(m_type.type &amp; c4::yml::KEYREF) != 0">m_key.anchor</Item>
<Item Name="val ref" Condition="(m_type.type &amp; c4::yml::VALREF) != 0">m_val.anchor</Item>
<Item Name="key anchor" Condition="(m_type.type &amp; c4::yml::KEYANCH) != 0">m_key.anchor</Item>
<Item Name="val anchor" Condition="(m_type.type &amp; c4::yml::VALANCH) != 0">m_val.anchor</Item>
<Item Name="parent" Condition="m_parent == c4::yml::NONE">NONE</Item>
<Item Name="parent" Condition="m_parent != c4::yml::NONE">m_parent</Item>
<Item Name="first child" Condition="m_first_child != c4::yml::NONE">m_first_child</Item>
<Item Name="last child" Condition="m_last_child != c4::yml::NONE">m_last_child</Item>
<Item Name="prev sibling" Condition="m_prev_sibling != c4::yml::NONE">m_prev_sibling</Item>
<Item Name="next sibling" Condition="m_next_sibling != c4::yml::NONE">m_next_sibling</Item>
</Expand>
</Type>
<Type Name="c4::yml::Tree">
<DisplayString>sz={m_size}, cap={m_cap}</DisplayString>
<Expand>
<Item Name="[size]">m_size</Item>
<Item Name="[capacity]">m_cap</Item>
<Synthetic Name="[buffer]">
<Expand>
<ArrayItems>
<Size>m_cap</Size>
<ValuePointer>m_buf</ValuePointer>
</ArrayItems>
</Expand>
</Synthetic>
<Item Name="free head">m_free_head</Item>
<Item Name="arena">m_arena</Item>
</Expand>
</Type>
<Type Name="c4::yml::Tree::_lookup_path_token">
<DisplayString>{value} ({type})</DisplayString>
<Expand>
<Item Name="value">value</Item>
<Item Name="type">type</Item>
</Expand>
</Type>
<Type Name="c4::yml::Tree::lookup_result">
<DisplayString>{path} -- target={target} closest={closest}</DisplayString>
<Expand>
<Item Name="target">target</Item>
<Item Name="closest">closest</Item>
<Item Name="path_pos">path_pos</Item>
<Item Name="path">path</Item>
<Synthetic Name="[resolved]">
<DisplayString>{path.str,[path_pos]}</DisplayString>
</Synthetic>
<Synthetic Name="[unresolved]">
<DisplayString>{path.str+path_pos,[path.len-path_pos]}</DisplayString>
</Synthetic>
</Expand>
</Type>
<Type Name="c4::yml::NodeRef">
<DisplayString Condition="(m_id == c4::yml::NONE)">(void)</DisplayString>
<DisplayString Condition="(m_seed.len != c4::yml::NONE) &amp;&amp; (m_seed.str == nullptr)">[INDEX SEED for] {*(m_tree->m_buf + m_id)}</DisplayString>
<DisplayString Condition="(m_seed.len != c4::yml::NONE) &amp;&amp; (m_seed.str != nullptr)">[NAMED SEED for] {*(m_tree->m_buf + m_id)}</DisplayString>
<DisplayString>{*(m_tree->m_buf + m_id)}</DisplayString>
<Expand>
<Item Name="id">m_id</Item>
<Item Name="element">*(m_tree->m_buf + m_id)</Item>
<Item Name="tree">m_tree</Item>
<Synthetic Name="[children]" Condition="(m_id != c4::yml::NONE) &amp;&amp; (((m_tree->m_buf + m_id)->m_type.type &amp; (c4::yml::MAP|c4::yml::SEQ)) != 0)">
<Expand>
<CustomListItems>
<Variable Name="tree" InitialValue="m_tree"/>
<Variable Name="buf" InitialValue="m_tree->m_buf"/>
<Variable Name="curr" InitialValue="(m_tree->m_buf + m_id)->m_first_child"/>
<Loop>
<Item>buf + curr</Item>
<Exec>curr = (buf + curr)->m_next_sibling</Exec>
<Break Condition="curr == c4::yml::NONE"/>
</Loop>
</CustomListItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="c4::yml::ConstNodeRef">
<DisplayString Condition="(m_id == c4::yml::NONE)">(void)</DisplayString>
<DisplayString>{*(m_tree->m_buf + m_id)}</DisplayString>
<Expand>
<Item Name="id">m_id</Item>
<Item Name="element">*(m_tree->m_buf + m_id)</Item>
<Item Name="tree">m_tree</Item>
<Synthetic Name="[children]" Condition="(m_id != c4::yml::NONE) &amp;&amp; (((m_tree->m_buf + m_id)->m_type.type &amp; (c4::yml::MAP|c4::yml::SEQ)) != 0)">
<Expand>
<CustomListItems>
<Variable Name="tree" InitialValue="m_tree"/>
<Variable Name="buf" InitialValue="m_tree->m_buf"/>
<Variable Name="curr" InitialValue="(m_tree->m_buf + m_id)->m_first_child"/>
<Loop>
<Item>buf + curr</Item>
<Exec>curr = (buf + curr)->m_next_sibling</Exec>
<Break Condition="curr == c4::yml::NONE"/>
</Loop>
</CustomListItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="c4::yml::detail::ReferenceResolver">
<DisplayString>#refs={refs.m_size} #nodes={t->m_size}</DisplayString>
<Expand>
<Synthetic Name="[ref_nodes]">
<Expand>
<CustomListItems>
<Variable Name="curr" InitialValue="0"/>
<Loop>
<Item>t->m_buf + (refs.m_stack + curr)->node</Item>
<Exec>curr = curr+1</Exec>
<Break Condition="curr >= refs.m_size"/>
</Loop>
</CustomListItems>
</Expand>
</Synthetic>
<Synthetic Name="[refs]">
<Expand>
<ArrayItems>
<Size>refs.m_size</Size>
<ValuePointer>refs.m_stack</ValuePointer>
</ArrayItems>
</Expand>
</Synthetic>
<Item Name="[tree]">t</Item>
</Expand>
</Type>
<Type Name="c4::yml::detail::stack&lt;*,*&gt;">
<DisplayString>sz={m_size} cap={m_capacity}</DisplayString>
<Expand>
<Item Name="[size]">m_size</Item>
<Item Name="[capacity]">m_capacity</Item>
<Item Name="[is small]">m_buf == m_stack</Item>
<Synthetic Name="[items]">
<Expand>
<ArrayItems>
<Size>m_size</Size>
<ValuePointer>m_stack</ValuePointer>
</ArrayItems>
</Expand>
</Synthetic>
</Expand>
</Type>
</AutoVisualizer>

View File

@ -0,0 +1,6 @@
#ifndef _RYML_STD_HPP_
#define _RYML_STD_HPP_
#include "./c4/yml/std/std.hpp"
#endif /* _RYML_STD_HPP_ */

@ -1 +0,0 @@
Subproject commit 213b201d264139cd1b887790197e08850af628e3

124
3rdparty/rapidyaml/rapidyaml.vcxproj vendored Normal file
View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(SolutionDir)common\vsprops\BaseProjectConfig.props" />
<Import Project="$(SolutionDir)common\vsprops\WinSDK.props" />
<PropertyGroup Label="Globals">
<ProjectGuid>{DE9653B6-17DD-356A-9EE0-28A731772587}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<ProjectName>rapidyaml</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset Condition="!$(Configuration.Contains(Clang))">$(DefaultPlatformToolset)</PlatformToolset>
<PlatformToolset Condition="$(Configuration.Contains(Clang))">ClangCL</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization Condition="$(Configuration.Contains(Release))">true</WholeProgramOptimization>
<UseDebugLibraries Condition="$(Configuration.Contains(Debug))">true</UseDebugLibraries>
<UseDebugLibraries Condition="!$(Configuration.Contains(Debug))">false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="PropertySheets">
<Import Project="..\DefaultProjectRootDir.props" />
<Import Project="..\3rdparty.props" />
<Import Condition="$(Configuration.Contains(Debug))" Project="..\..\common\vsprops\CodeGen_Debug.props" />
<Import Condition="$(Configuration.Contains(Devel))" Project="..\..\common\vsprops\CodeGen_Devel.props" />
<Import Condition="$(Configuration.Contains(Release))" Project="..\..\common\vsprops\CodeGen_Release.props" />
<Import Condition="!$(Configuration.Contains(Release))" Project="..\..\common\vsprops\IncrementalLinking.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>%(PreprocessorDefinitions);C4_NO_DEBUG_BREAK</PreprocessorDefinitions>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(ProjectDir)src;$(ProjectDir)include;$(ProjectDir)..\fast_float\include</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="$(Configuration.Contains(Debug))">
<ClCompile>
<PreprocessorDefinitions>%(PreprocessorDefinitions);WIN32;_WINDOWS</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="!$(Configuration.Contains(Debug))">
<ClCompile>
<PreprocessorDefinitions>%(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="include\c4\base64.hpp" />
<ClInclude Include="include\c4\blob.hpp" />
<ClInclude Include="include\c4\charconv.hpp" />
<ClInclude Include="include\c4\compiler.hpp" />
<ClInclude Include="include\c4\config.hpp" />
<ClInclude Include="include\c4\cpu.hpp" />
<ClInclude Include="include\c4\dump.hpp" />
<ClInclude Include="include\c4\error.hpp" />
<ClInclude Include="include\c4\export.hpp" />
<ClInclude Include="include\c4\format.hpp" />
<ClInclude Include="include\c4\language.hpp" />
<ClInclude Include="include\c4\memory_util.hpp" />
<ClInclude Include="include\c4\platform.hpp" />
<ClInclude Include="include\c4\preprocessor.hpp" />
<ClInclude Include="include\c4\std\std.hpp" />
<ClInclude Include="include\c4\std\std_fwd.hpp" />
<ClInclude Include="include\c4\std\string.hpp" />
<ClInclude Include="include\c4\std\string_fwd.hpp" />
<ClInclude Include="include\c4\std\string_view.hpp" />
<ClInclude Include="include\c4\std\tuple.hpp" />
<ClInclude Include="include\c4\std\vector.hpp" />
<ClInclude Include="include\c4\std\vector_fwd.hpp" />
<ClInclude Include="include\c4\substr.hpp" />
<ClInclude Include="include\c4\substr_fwd.hpp" />
<ClInclude Include="include\c4\szconv.hpp" />
<ClInclude Include="include\c4\types.hpp" />
<ClInclude Include="include\c4\utf.hpp" />
<ClInclude Include="include\c4\windows.hpp" />
<ClInclude Include="include\c4\windows_pop.hpp" />
<ClInclude Include="include\c4\windows_push.hpp" />
<ClInclude Include="include\c4\yml\common.hpp" />
<ClInclude Include="include\c4\yml\detail\checks.hpp" />
<ClInclude Include="include\c4\yml\detail\parser_dbg.hpp" />
<ClInclude Include="include\c4\yml\detail\print.hpp" />
<ClInclude Include="include\c4\yml\detail\stack.hpp" />
<ClInclude Include="include\c4\yml\emit.def.hpp" />
<ClInclude Include="include\c4\yml\emit.hpp" />
<ClInclude Include="include\c4\yml\export.hpp" />
<ClInclude Include="include\c4\yml\node.hpp" />
<ClInclude Include="include\c4\yml\parse.hpp" />
<ClInclude Include="include\c4\yml\preprocess.hpp" />
<ClInclude Include="include\c4\yml\std\map.hpp" />
<ClInclude Include="include\c4\yml\std\std.hpp" />
<ClInclude Include="include\c4\yml\std\string.hpp" />
<ClInclude Include="include\c4\yml\std\vector.hpp" />
<ClInclude Include="include\c4\yml\tree.hpp" />
<ClInclude Include="include\c4\yml\writer.hpp" />
<ClInclude Include="include\c4\yml\yml.hpp" />
<ClInclude Include="include\ryml.hpp" />
<ClInclude Include="include\ryml_std.hpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="include\c4\c4core.natvis" />
<Natvis Include="include\ryml.natvis" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\c4\base64.cpp" />
<ClCompile Include="src\c4\error.cpp" />
<ClCompile Include="src\c4\format.cpp" />
<ClCompile Include="src\c4\language.cpp" />
<ClCompile Include="src\c4\memory_util.cpp" />
<ClCompile Include="src\c4\utf.cpp" />
<ClCompile Include="src\c4\yml\common.cpp" />
<ClCompile Include="src\c4\yml\node.cpp" />
<ClCompile Include="src\c4\yml\parse.cpp" />
<ClCompile Include="src\c4\yml\preprocess.cpp" />
<ClCompile Include="src\c4\yml\tree.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -1,139 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(SolutionDir)common\vsprops\BaseProjectConfig.props" />
<Import Project="$(SolutionDir)common\vsprops\WinSDK.props" />
<PropertyGroup Label="Globals">
<ProjectGuid>{DE9653B6-17DD-356A-9EE0-28A731772587}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset Condition="!$(Configuration.Contains(Clang))">$(DefaultPlatformToolset)</PlatformToolset>
<PlatformToolset Condition="$(Configuration.Contains(Clang))">ClangCL</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization Condition="$(Configuration.Contains(Release))">true</WholeProgramOptimization>
<UseDebugLibraries Condition="$(Configuration.Contains(Debug))">true</UseDebugLibraries>
<UseDebugLibraries Condition="!$(Configuration.Contains(Debug))">false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="PropertySheets">
<Import Project="..\DefaultProjectRootDir.props" />
<Import Project="..\3rdparty.props" />
<Import Condition="$(Configuration.Contains(Debug))" Project="..\..\common\vsprops\CodeGen_Debug.props" />
<Import Condition="$(Configuration.Contains(Devel))" Project="..\..\common\vsprops\CodeGen_Devel.props" />
<Import Condition="$(Configuration.Contains(Release))" Project="..\..\common\vsprops\CodeGen_Release.props" />
<Import Condition="!$(Configuration.Contains(Release))" Project="..\..\common\vsprops\IncrementalLinking.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<AdditionalIncludeDirectories>$(ProjectDir)/rapidyaml/src;$(ProjectDir)/rapidyaml/ext/c4core/src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="$(Configuration.Contains(Debug))">
<ClCompile>
<PreprocessorDefinitions>%(PreprocessorDefinitions);WIN32;_WINDOWS</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="!$(Configuration.Contains(Debug))">
<ClCompile>
<PreprocessorDefinitions>%(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="rapidyaml\src\ryml.hpp" />
<ClInclude Include="rapidyaml\src\ryml_std.hpp" />
<ClInclude Include="rapidyaml\src\c4\yml\detail\checks.hpp" />
<ClInclude Include="rapidyaml\src\c4\yml\detail\parser_dbg.hpp" />
<ClInclude Include="rapidyaml\src\c4\yml\detail\print.hpp" />
<ClInclude Include="rapidyaml\src\c4\yml\detail\stack.hpp" />
<ClInclude Include="rapidyaml\src\c4\yml\common.hpp" />
<ClCompile Include="rapidyaml\src\c4\yml\common.cpp" />
<ClInclude Include="rapidyaml\src\c4\yml\emit.def.hpp" />
<ClInclude Include="rapidyaml\src\c4\yml\emit.hpp" />
<ClInclude Include="rapidyaml\src\c4\yml\export.hpp" />
<ClInclude Include="rapidyaml\src\c4\yml\node.hpp" />
<ClCompile Include="rapidyaml\src\c4\yml\node.cpp" />
<ClInclude Include="rapidyaml\src\c4\yml\parse.hpp" />
<ClCompile Include="rapidyaml\src\c4\yml\parse.cpp" />
<ClInclude Include="rapidyaml\src\c4\yml\preprocess.hpp" />
<ClCompile Include="rapidyaml\src\c4\yml\preprocess.cpp" />
<ClInclude Include="rapidyaml\src\c4\yml\std\map.hpp" />
<ClInclude Include="rapidyaml\src\c4\yml\std\std.hpp" />
<ClInclude Include="rapidyaml\src\c4\yml\std\string.hpp" />
<ClInclude Include="rapidyaml\src\c4\yml\std\vector.hpp" />
<ClInclude Include="rapidyaml\src\c4\yml\tree.hpp" />
<ClCompile Include="rapidyaml\src\c4\yml\tree.cpp" />
<ClInclude Include="rapidyaml\src\c4\yml\writer.hpp" />
<ClInclude Include="rapidyaml\src\c4\yml\yml.hpp" />
<Natvis Include="rapidyaml\src\ryml.natvis"></Natvis>
<ClInclude Include="rapidyaml\ext\c4core\src\c4\allocator.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\base64.hpp" />
<ClCompile Include="rapidyaml\ext\c4core\src\c4\base64.cpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\blob.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\bitmask.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\charconv.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\c4_pop.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\c4_push.hpp" />
<ClCompile Include="rapidyaml\ext\c4core\src\c4\char_traits.cpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\char_traits.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\common.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\compiler.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\config.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\cpu.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\ctor_dtor.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\dump.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\enum.hpp" />
<ClCompile Include="rapidyaml\ext\c4core\src\c4\error.cpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\error.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\export.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\format.hpp" />
<ClCompile Include="rapidyaml\ext\c4core\src\c4\format.cpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\hash.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\language.hpp" />
<ClCompile Include="rapidyaml\ext\c4core\src\c4\language.cpp" />
<ClCompile Include="rapidyaml\ext\c4core\src\c4\memory_resource.cpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\memory_resource.hpp" />
<ClCompile Include="rapidyaml\ext\c4core\src\c4\memory_util.cpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\memory_util.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\platform.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\preprocessor.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\restrict.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\span.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\std\std.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\std\std_fwd.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\std\string.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\std\string_fwd.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\std\tuple.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\std\vector.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\std\vector_fwd.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\substr.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\substr_fwd.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\szconv.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\type_name.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\types.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\unrestrict.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\utf.hpp" />
<ClCompile Include="rapidyaml\ext\c4core\src\c4\utf.cpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\windows.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\windows_pop.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\windows_push.hpp" />
<Natvis Include="rapidyaml\ext\c4core\src\c4\c4core.natvis"></Natvis>
<ClInclude Include="rapidyaml\ext\c4core\src\c4\ext\debugbreak\debugbreak.h" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\ext\rng\rng.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\ext\sg14\inplace_function.h" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\ext\fast_float.hpp" />
<ClInclude Include="rapidyaml\ext\c4core\src\c4\ext\fast_float_all.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

221
3rdparty/rapidyaml/src/c4/base64.cpp vendored Normal file
View File

@ -0,0 +1,221 @@
#include "c4/base64.hpp"
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wchar-subscripts" // array subscript is of type 'char'
# pragma clang diagnostic ignored "-Wold-style-cast"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wchar-subscripts"
# pragma GCC diagnostic ignored "-Wtype-limits"
# pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
namespace c4 {
namespace detail {
constexpr static const char base64_sextet_to_char_[64] = {
/* 0/ 65*/ 'A', /* 1/ 66*/ 'B', /* 2/ 67*/ 'C', /* 3/ 68*/ 'D',
/* 4/ 69*/ 'E', /* 5/ 70*/ 'F', /* 6/ 71*/ 'G', /* 7/ 72*/ 'H',
/* 8/ 73*/ 'I', /* 9/ 74*/ 'J', /*10/ 75*/ 'K', /*11/ 74*/ 'L',
/*12/ 77*/ 'M', /*13/ 78*/ 'N', /*14/ 79*/ 'O', /*15/ 78*/ 'P',
/*16/ 81*/ 'Q', /*17/ 82*/ 'R', /*18/ 83*/ 'S', /*19/ 82*/ 'T',
/*20/ 85*/ 'U', /*21/ 86*/ 'V', /*22/ 87*/ 'W', /*23/ 88*/ 'X',
/*24/ 89*/ 'Y', /*25/ 90*/ 'Z', /*26/ 97*/ 'a', /*27/ 98*/ 'b',
/*28/ 99*/ 'c', /*29/100*/ 'd', /*30/101*/ 'e', /*31/102*/ 'f',
/*32/103*/ 'g', /*33/104*/ 'h', /*34/105*/ 'i', /*35/106*/ 'j',
/*36/107*/ 'k', /*37/108*/ 'l', /*38/109*/ 'm', /*39/110*/ 'n',
/*40/111*/ 'o', /*41/112*/ 'p', /*42/113*/ 'q', /*43/114*/ 'r',
/*44/115*/ 's', /*45/116*/ 't', /*46/117*/ 'u', /*47/118*/ 'v',
/*48/119*/ 'w', /*49/120*/ 'x', /*50/121*/ 'y', /*51/122*/ 'z',
/*52/ 48*/ '0', /*53/ 49*/ '1', /*54/ 50*/ '2', /*55/ 51*/ '3',
/*56/ 52*/ '4', /*57/ 53*/ '5', /*58/ 54*/ '6', /*59/ 55*/ '7',
/*60/ 56*/ '8', /*61/ 57*/ '9', /*62/ 43*/ '+', /*63/ 47*/ '/',
};
// https://www.cs.cmu.edu/~pattis/15-1XX/common/handouts/ascii.html
constexpr static const char base64_char_to_sextet_[128] = {
#define __ char(-1) // undefined below
/* 0 NUL*/ __, /* 1 SOH*/ __, /* 2 STX*/ __, /* 3 ETX*/ __,
/* 4 EOT*/ __, /* 5 ENQ*/ __, /* 6 ACK*/ __, /* 7 BEL*/ __,
/* 8 BS */ __, /* 9 TAB*/ __, /* 10 LF */ __, /* 11 VT */ __,
/* 12 FF */ __, /* 13 CR */ __, /* 14 SO */ __, /* 15 SI */ __,
/* 16 DLE*/ __, /* 17 DC1*/ __, /* 18 DC2*/ __, /* 19 DC3*/ __,
/* 20 DC4*/ __, /* 21 NAK*/ __, /* 22 SYN*/ __, /* 23 ETB*/ __,
/* 24 CAN*/ __, /* 25 EM */ __, /* 26 SUB*/ __, /* 27 ESC*/ __,
/* 28 FS */ __, /* 29 GS */ __, /* 30 RS */ __, /* 31 US */ __,
/* 32 SPC*/ __, /* 33 ! */ __, /* 34 " */ __, /* 35 # */ __,
/* 36 $ */ __, /* 37 % */ __, /* 38 & */ __, /* 39 ' */ __,
/* 40 ( */ __, /* 41 ) */ __, /* 42 * */ __, /* 43 + */ 62,
/* 44 , */ __, /* 45 - */ __, /* 46 . */ __, /* 47 / */ 63,
/* 48 0 */ 52, /* 49 1 */ 53, /* 50 2 */ 54, /* 51 3 */ 55,
/* 52 4 */ 56, /* 53 5 */ 57, /* 54 6 */ 58, /* 55 7 */ 59,
/* 56 8 */ 60, /* 57 9 */ 61, /* 58 : */ __, /* 59 ; */ __,
/* 60 < */ __, /* 61 = */ __, /* 62 > */ __, /* 63 ? */ __,
/* 64 @ */ __, /* 65 A */ 0, /* 66 B */ 1, /* 67 C */ 2,
/* 68 D */ 3, /* 69 E */ 4, /* 70 F */ 5, /* 71 G */ 6,
/* 72 H */ 7, /* 73 I */ 8, /* 74 J */ 9, /* 75 K */ 10,
/* 76 L */ 11, /* 77 M */ 12, /* 78 N */ 13, /* 79 O */ 14,
/* 80 P */ 15, /* 81 Q */ 16, /* 82 R */ 17, /* 83 S */ 18,
/* 84 T */ 19, /* 85 U */ 20, /* 86 V */ 21, /* 87 W */ 22,
/* 88 X */ 23, /* 89 Y */ 24, /* 90 Z */ 25, /* 91 [ */ __,
/* 92 \ */ __, /* 93 ] */ __, /* 94 ^ */ __, /* 95 _ */ __,
/* 96 ` */ __, /* 97 a */ 26, /* 98 b */ 27, /* 99 c */ 28,
/*100 d */ 29, /*101 e */ 30, /*102 f */ 31, /*103 g */ 32,
/*104 h */ 33, /*105 i */ 34, /*106 j */ 35, /*107 k */ 36,
/*108 l */ 37, /*109 m */ 38, /*110 n */ 39, /*111 o */ 40,
/*112 p */ 41, /*113 q */ 42, /*114 r */ 43, /*115 s */ 44,
/*116 t */ 45, /*117 u */ 46, /*118 v */ 47, /*119 w */ 48,
/*120 x */ 49, /*121 y */ 50, /*122 z */ 51, /*123 { */ __,
/*124 | */ __, /*125 } */ __, /*126 ~ */ __, /*127 DEL*/ __,
#undef __
};
#ifndef NDEBUG
void base64_test_tables()
{
for(size_t i = 0; i < C4_COUNTOF(detail::base64_sextet_to_char_); ++i)
{
char s2c = base64_sextet_to_char_[i];
char c2s = base64_char_to_sextet_[(int)s2c];
C4_CHECK((size_t)c2s == i);
}
for(size_t i = 0; i < C4_COUNTOF(detail::base64_char_to_sextet_); ++i)
{
char c2s = base64_char_to_sextet_[i];
if(c2s == char(-1))
continue;
char s2c = base64_sextet_to_char_[(int)c2s];
C4_CHECK((size_t)s2c == i);
}
}
#endif
} // namespace detail
bool base64_valid(csubstr encoded)
{
if(encoded.len & 3u) // (encoded.len % 4u)
return false;
for(const char c : encoded)
{
if(c < 0/* || c >= 128*/)
return false;
if(c == '=')
continue;
if(detail::base64_char_to_sextet_[c] == char(-1))
return false;
}
return true;
}
size_t base64_encode(substr buf, cblob data)
{
#define c4append_(c) { if(pos < buf.len) { buf.str[pos] = (c); } ++pos; }
#define c4append_idx_(char_idx) \
{\
C4_XASSERT((char_idx) < sizeof(detail::base64_sextet_to_char_));\
c4append_(detail::base64_sextet_to_char_[(char_idx)]);\
}
size_t rem, pos = 0;
constexpr const uint32_t sextet_mask = uint32_t(1 << 6) - 1;
const unsigned char *C4_RESTRICT d = (const unsigned char *) data.buf; // cast to unsigned to avoid wrapping high-bits
for(rem = data.len; rem >= 3; rem -= 3, d += 3)
{
const uint32_t val = ((uint32_t(d[0]) << 16) | (uint32_t(d[1]) << 8) | (uint32_t(d[2])));
c4append_idx_((val >> 18) & sextet_mask);
c4append_idx_((val >> 12) & sextet_mask);
c4append_idx_((val >> 6) & sextet_mask);
c4append_idx_((val ) & sextet_mask);
}
C4_ASSERT(rem < 3);
if(rem == 2)
{
const uint32_t val = ((uint32_t(d[0]) << 16) | (uint32_t(d[1]) << 8));
c4append_idx_((val >> 18) & sextet_mask);
c4append_idx_((val >> 12) & sextet_mask);
c4append_idx_((val >> 6) & sextet_mask);
c4append_('=');
}
else if(rem == 1)
{
const uint32_t val = ((uint32_t(d[0]) << 16));
c4append_idx_((val >> 18) & sextet_mask);
c4append_idx_((val >> 12) & sextet_mask);
c4append_('=');
c4append_('=');
}
return pos;
#undef c4append_
#undef c4append_idx_
}
size_t base64_decode(csubstr encoded, blob data)
{
#define c4append_(c) { if(wpos < data.len) { data.buf[wpos] = static_cast<c4::byte>(c); } ++wpos; }
#define c4appendval_(c, shift)\
{\
C4_XASSERT(c >= 0);\
C4_XASSERT(size_t(c) < sizeof(detail::base64_char_to_sextet_));\
val |= static_cast<uint32_t>(detail::base64_char_to_sextet_[(c)]) << ((shift) * 6);\
}
C4_ASSERT(base64_valid(encoded));
C4_CHECK((encoded.len & 3u) == 0);
size_t wpos = 0; // the write position
const char *C4_RESTRICT d = encoded.str;
constexpr const uint32_t full_byte = 0xff;
// process every quartet of input 6 bits --> triplet of output bytes
for(size_t rpos = 0; rpos < encoded.len; rpos += 4, d += 4)
{
if(d[2] == '=' || d[3] == '=') // skip the last quartet if it is padded
{
C4_ASSERT(d + 4 == encoded.str + encoded.len);
break;
}
uint32_t val = 0;
c4appendval_(d[3], 0);
c4appendval_(d[2], 1);
c4appendval_(d[1], 2);
c4appendval_(d[0], 3);
c4append_((val >> (2 * 8)) & full_byte);
c4append_((val >> (1 * 8)) & full_byte);
c4append_((val ) & full_byte);
}
// deal with the last quartet when it is padded
if(d == encoded.str + encoded.len)
return wpos;
if(d[2] == '=') // 2 padding chars
{
C4_ASSERT(d + 4 == encoded.str + encoded.len);
C4_ASSERT(d[3] == '=');
uint32_t val = 0;
c4appendval_(d[1], 2);
c4appendval_(d[0], 3);
c4append_((val >> (2 * 8)) & full_byte);
}
else if(d[3] == '=') // 1 padding char
{
C4_ASSERT(d + 4 == encoded.str + encoded.len);
uint32_t val = 0;
c4appendval_(d[2], 1);
c4appendval_(d[1], 2);
c4appendval_(d[0], 3);
c4append_((val >> (2 * 8)) & full_byte);
c4append_((val >> (1 * 8)) & full_byte);
}
return wpos;
#undef c4append_
#undef c4appendval_
}
} // namespace c4
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif

234
3rdparty/rapidyaml/src/c4/error.cpp vendored Normal file
View File

@ -0,0 +1,234 @@
#include "c4/error.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#define C4_LOGF_ERR(...) fprintf(stderr, __VA_ARGS__); fflush(stderr)
#define C4_LOGF_WARN(...) fprintf(stderr, __VA_ARGS__); fflush(stderr)
#define C4_LOGP(msg, ...) printf(msg)
#if defined(C4_XBOX) || (defined(C4_WIN) && defined(C4_MSVC))
# include "c4/windows.hpp"
#elif defined(C4_PS4)
# include <libdbg.h>
#elif defined(C4_UNIX) || defined(C4_LINUX)
# include <sys/stat.h>
# include <cstring>
# include <fcntl.h>
#elif defined(C4_MACOS) || defined(C4_IOS)
# include <assert.h>
# include <stdbool.h>
# include <sys/types.h>
# include <sys/sysctl.h>
#endif
// the amalgamation tool is dumb and was omitting this include under MACOS.
// So do it only once:
#if defined(C4_UNIX) || defined(C4_LINUX) || defined(C4_MACOS) || defined(C4_IOS)
# include <unistd.h>
#endif
#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION)
# include <exception>
#endif
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wformat-nonliteral"
# pragma clang diagnostic ignored "-Wold-style-cast"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
# pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
//-----------------------------------------------------------------------------
namespace c4 {
static error_flags s_error_flags = ON_ERROR_DEFAULTS;
static error_callback_type s_error_callback = nullptr;
//-----------------------------------------------------------------------------
error_flags get_error_flags()
{
return s_error_flags;
}
void set_error_flags(error_flags flags)
{
s_error_flags = flags;
}
error_callback_type get_error_callback()
{
return s_error_callback;
}
/** Set the function which is called when an error occurs. */
void set_error_callback(error_callback_type cb)
{
s_error_callback = cb;
}
//-----------------------------------------------------------------------------
void handle_error(srcloc where, const char *fmt, ...)
{
char buf[1024];
size_t msglen = 0;
if(s_error_flags & (ON_ERROR_LOG|ON_ERROR_CALLBACK))
{
va_list args;
va_start(args, fmt);
int ilen = vsnprintf(buf, sizeof(buf), fmt, args); // ss.vprintf(fmt, args);
va_end(args);
msglen = ilen >= 0 && ilen < (int)sizeof(buf) ? static_cast<size_t>(ilen) : sizeof(buf)-1;
}
if(s_error_flags & ON_ERROR_LOG)
{
C4_LOGF_ERR("\n");
#if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC)
C4_LOGF_ERR("%s:%d: ERROR: %s\n", where.file, where.line, buf);
C4_LOGF_ERR("%s:%d: ERROR here: %s\n", where.file, where.line, where.func);
#elif defined(C4_ERROR_SHOWS_FILELINE)
C4_LOGF_ERR("%s:%d: ERROR: %s\n", where.file, where.line, buf);
#elif ! defined(C4_ERROR_SHOWS_FUNC)
C4_LOGF_ERR("ERROR: %s\n", buf);
#endif
}
if(s_error_flags & ON_ERROR_CALLBACK)
{
if(s_error_callback)
{
s_error_callback(buf, msglen/*ss.c_strp(), ss.tellp()*/);
}
}
if(s_error_flags & ON_ERROR_ABORT)
{
abort();
}
if(s_error_flags & ON_ERROR_THROW)
{
#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION)
throw std::runtime_error(buf);
#else
abort();
#endif
}
}
//-----------------------------------------------------------------------------
void handle_warning(srcloc where, const char *fmt, ...)
{
va_list args;
char buf[1024]; //sstream<c4::string> ss;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
C4_LOGF_WARN("\n");
#if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC)
C4_LOGF_WARN("%s:%d: WARNING: %s\n", where.file, where.line, buf/*ss.c_strp()*/);
C4_LOGF_WARN("%s:%d: WARNING: here: %s\n", where.file, where.line, where.func);
#elif defined(C4_ERROR_SHOWS_FILELINE)
C4_LOGF_WARN("%s:%d: WARNING: %s\n", where.file, where.line, buf/*ss.c_strp()*/);
#elif ! defined(C4_ERROR_SHOWS_FUNC)
C4_LOGF_WARN("WARNING: %s\n", buf/*ss.c_strp()*/);
#endif
//c4::log.flush();
}
//-----------------------------------------------------------------------------
bool is_debugger_attached()
{
#if defined(C4_UNIX) || defined(C4_LINUX)
static bool first_call = true;
static bool first_call_result = false;
if(first_call)
{
first_call = false;
C4_SUPPRESS_WARNING_GCC_PUSH
#if defined(__GNUC__) && __GNUC__ > 9
C4_SUPPRESS_WARNING_GCC("-Wanalyzer-fd-leak")
#endif
//! @see http://stackoverflow.com/questions/3596781/how-to-detect-if-the-current-process-is-being-run-by-gdb
//! (this answer: http://stackoverflow.com/a/24969863/3968589 )
char buf[1024] = "";
int status_fd = open("/proc/self/status", O_RDONLY);
if (status_fd == -1)
{
return 0;
}
else
{
ssize_t num_read = ::read(status_fd, buf, sizeof(buf));
if (num_read > 0)
{
static const char TracerPid[] = "TracerPid:";
char *tracer_pid;
if(num_read < 1024)
{
buf[num_read] = 0;
}
tracer_pid = strstr(buf, TracerPid);
if (tracer_pid)
{
first_call_result = !!::atoi(tracer_pid + sizeof(TracerPid) - 1);
}
}
close(status_fd);
}
C4_SUPPRESS_WARNING_GCC_POP
}
return first_call_result;
#elif defined(C4_PS4)
return (sceDbgIsDebuggerAttached() != 0);
#elif defined(C4_XBOX) || (defined(C4_WIN) && defined(C4_MSVC))
return IsDebuggerPresent() != 0;
#elif defined(C4_MACOS) || defined(C4_IOS)
// https://stackoverflow.com/questions/2200277/detecting-debugger-on-mac-os-x
// Returns true if the current process is being debugged (either
// running under the debugger or has a debugger attached post facto).
int junk;
int mib[4];
struct kinfo_proc info;
size_t size;
// Initialize the flags so that, if sysctl fails for some bizarre
// reason, we get a predictable result.
info.kp_proc.p_flag = 0;
// Initialize mib, which tells sysctl the info we want, in this case
// we're looking for information about a specific process ID.
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
// Call sysctl.
size = sizeof(info);
junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
assert(junk == 0);
// We're being debugged if the P_TRACED flag is set.
return ((info.kp_proc.p_flag & P_TRACED) != 0);
#else
return false;
#endif
} // is_debugger_attached()
} // namespace c4
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif

64
3rdparty/rapidyaml/src/c4/format.cpp vendored Normal file
View File

@ -0,0 +1,64 @@
#include "c4/format.hpp"
#include <memory> // for std::align
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wformat-nonliteral"
# pragma clang diagnostic ignored "-Wold-style-cast"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
# pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
namespace c4 {
size_t to_chars(substr buf, fmt::const_raw_wrapper r)
{
void * vptr = buf.str;
size_t space = buf.len;
auto ptr = (decltype(buf.str)) std::align(r.alignment, r.len, vptr, space);
if(ptr == nullptr)
{
// if it was not possible to align, return a conservative estimate
// of the required space
return r.alignment + r.len;
}
C4_CHECK(ptr >= buf.begin() && ptr <= buf.end());
size_t sz = static_cast<size_t>(ptr - buf.str) + r.len;
if(sz <= buf.len)
{
memcpy(ptr, r.buf, r.len);
}
return sz;
}
bool from_chars(csubstr buf, fmt::raw_wrapper *r)
{
C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wcast-qual")
void * vptr = (void*)buf.str;
C4_SUPPRESS_WARNING_GCC_POP
size_t space = buf.len;
auto ptr = (decltype(buf.str)) std::align(r->alignment, r->len, vptr, space);
C4_CHECK(ptr != nullptr);
C4_CHECK(ptr >= buf.begin() && ptr <= buf.end());
C4_SUPPRESS_WARNING_GCC_PUSH
#if defined(__GNUC__) && __GNUC__ > 9
C4_SUPPRESS_WARNING_GCC("-Wanalyzer-null-argument")
#endif
memcpy(r->buf, ptr, r->len);
C4_SUPPRESS_WARNING_GCC_POP
return true;
}
} // namespace c4
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif

16
3rdparty/rapidyaml/src/c4/language.cpp vendored Normal file
View File

@ -0,0 +1,16 @@
#include "c4/language.hpp"
namespace c4 {
namespace detail {
#ifndef __GNUC__
void use_char_pointer(char const volatile* v)
{
C4_UNUSED(v);
}
#else
void foo() {} // to avoid empty file warning from the linker
#endif
} // namespace detail
} // namespace c4

View File

@ -0,0 +1,32 @@
#include "c4/memory_util.hpp"
#include "c4/error.hpp"
namespace c4 {
/** Fills 'dest' with the first 'pattern_size' bytes at 'pattern', 'num_times'. */
void mem_repeat(void* dest, void const* pattern, size_t pattern_size, size_t num_times)
{
if(C4_UNLIKELY(num_times == 0))
return;
C4_ASSERT( ! mem_overlaps(dest, pattern, num_times*pattern_size, pattern_size));
char *begin = static_cast<char*>(dest);
char *end = begin + num_times * pattern_size;
// copy the pattern once
::memcpy(begin, pattern, pattern_size);
// now copy from dest to itself, doubling up every time
size_t n = pattern_size;
while(begin + 2*n < end)
{
::memcpy(begin + n, begin, n);
n <<= 1; // double n
}
// copy the missing part
if(begin + n < end)
{
::memcpy(begin + n, begin, static_cast<size_t>(end - (begin + n)));
}
}
} // namespace c4

60
3rdparty/rapidyaml/src/c4/utf.cpp vendored Normal file
View File

@ -0,0 +1,60 @@
#include "c4/utf.hpp"
#include "c4/charconv.hpp"
namespace c4 {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
size_t decode_code_point(uint8_t *C4_RESTRICT buf, size_t buflen, const uint32_t code)
{
C4_UNUSED(buflen);
C4_ASSERT(buflen >= 4);
if (code <= UINT32_C(0x7f))
{
buf[0] = (uint8_t)code;
return 1u;
}
else if(code <= UINT32_C(0x7ff))
{
buf[0] = (uint8_t)(UINT32_C(0xc0) | (code >> 6)); /* 110xxxxx */
buf[1] = (uint8_t)(UINT32_C(0x80) | (code & UINT32_C(0x3f))); /* 10xxxxxx */
return 2u;
}
else if(code <= UINT32_C(0xffff))
{
buf[0] = (uint8_t)(UINT32_C(0xe0) | ((code >> 12))); /* 1110xxxx */
buf[1] = (uint8_t)(UINT32_C(0x80) | ((code >> 6) & UINT32_C(0x3f))); /* 10xxxxxx */
buf[2] = (uint8_t)(UINT32_C(0x80) | ((code ) & UINT32_C(0x3f))); /* 10xxxxxx */
return 3u;
}
else if(code <= UINT32_C(0x10ffff))
{
buf[0] = (uint8_t)(UINT32_C(0xf0) | ((code >> 18))); /* 11110xxx */
buf[1] = (uint8_t)(UINT32_C(0x80) | ((code >> 12) & UINT32_C(0x3f))); /* 10xxxxxx */
buf[2] = (uint8_t)(UINT32_C(0x80) | ((code >> 6) & UINT32_C(0x3f))); /* 10xxxxxx */
buf[3] = (uint8_t)(UINT32_C(0x80) | ((code ) & UINT32_C(0x3f))); /* 10xxxxxx */
return 4u;
}
return 0;
}
substr decode_code_point(substr out, csubstr code_point)
{
C4_ASSERT(out.len >= 4);
C4_ASSERT(!code_point.begins_with("U+"));
C4_ASSERT(!code_point.begins_with("\\x"));
C4_ASSERT(!code_point.begins_with("\\u"));
C4_ASSERT(!code_point.begins_with("\\U"));
C4_ASSERT(!code_point.begins_with('0'));
C4_ASSERT(code_point.len <= 8);
C4_ASSERT(code_point.len > 0);
uint32_t code_point_val;
C4_CHECK(read_hex(code_point, &code_point_val));
size_t ret = decode_code_point((uint8_t*)out.str, out.len, code_point_val);
C4_ASSERT(ret <= 4);
return out.first(ret);
}
C4_SUPPRESS_WARNING_GCC_CLANG_POP
} // namespace c4

143
3rdparty/rapidyaml/src/c4/yml/common.cpp vendored Normal file
View File

@ -0,0 +1,143 @@
#include "c4/yml/common.hpp"
#ifndef RYML_NO_DEFAULT_CALLBACKS
# include <stdlib.h>
# include <stdio.h>
# ifdef RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS
# include <stdexcept>
# endif
#endif // RYML_NO_DEFAULT_CALLBACKS
namespace c4 {
namespace yml {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(4702/*unreachable code*/) // on the call to the unreachable macro
namespace {
Callbacks s_default_callbacks;
} // anon namespace
#ifndef RYML_NO_DEFAULT_CALLBACKS
void report_error_impl(const char* msg, size_t length, Location loc, FILE *f)
{
if(!f)
f = stderr;
if(loc)
{
if(!loc.name.empty())
{
fwrite(loc.name.str, 1, loc.name.len, f);
fputc(':', f);
}
fprintf(f, "%zu:", loc.line);
if(loc.col)
fprintf(f, "%zu:", loc.col);
if(loc.offset)
fprintf(f, " (%zuB):", loc.offset);
}
fprintf(f, "%.*s\n", (int)length, msg);
fflush(f);
}
[[noreturn]] void error_impl(const char* msg, size_t length, Location loc, void * /*user_data*/)
{
report_error_impl(msg, length, loc, nullptr);
#ifdef RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS
throw std::runtime_error(std::string(msg, length));
#else
::abort();
#endif
}
void* allocate_impl(size_t length, void * /*hint*/, void * /*user_data*/)
{
void *mem = ::malloc(length);
if(mem == nullptr)
{
const char msg[] = "could not allocate memory";
error_impl(msg, sizeof(msg)-1, {}, nullptr);
}
return mem;
}
void free_impl(void *mem, size_t /*length*/, void * /*user_data*/)
{
::free(mem);
}
#endif // RYML_NO_DEFAULT_CALLBACKS
Callbacks::Callbacks()
:
m_user_data(nullptr),
#ifndef RYML_NO_DEFAULT_CALLBACKS
m_allocate(allocate_impl),
m_free(free_impl),
m_error(error_impl)
#else
m_allocate(nullptr),
m_free(nullptr),
m_error(nullptr)
#endif
{
}
Callbacks::Callbacks(void *user_data, pfn_allocate alloc_, pfn_free free_, pfn_error error_)
:
m_user_data(user_data),
#ifndef RYML_NO_DEFAULT_CALLBACKS
m_allocate(alloc_ ? alloc_ : allocate_impl),
m_free(free_ ? free_ : free_impl),
m_error((error_ ? error_ : error_impl))
#else
m_allocate(alloc_),
m_free(free_),
m_error(error_)
#endif
{
C4_CHECK(m_allocate);
C4_CHECK(m_free);
C4_CHECK(m_error);
}
void set_callbacks(Callbacks const& c)
{
s_default_callbacks = c;
}
Callbacks const& get_callbacks()
{
return s_default_callbacks;
}
void reset_callbacks()
{
set_callbacks(Callbacks());
}
// the [[noreturn]] attribute needs to be here as well (UB otherwise)
// https://en.cppreference.com/w/cpp/language/attributes/noreturn
[[noreturn]] void error(Callbacks const& cb, const char *msg, size_t msg_len, Location loc)
{
cb.m_error(msg, msg_len, loc, cb.m_user_data);
abort(); // call abort in case the error callback didn't interrupt execution
C4_UNREACHABLE();
}
// the [[noreturn]] attribute needs to be here as well (UB otherwise)
// see https://en.cppreference.com/w/cpp/language/attributes/noreturn
[[noreturn]] void error(const char *msg, size_t msg_len, Location loc)
{
error(s_default_callbacks, msg, msg_len, loc);
C4_UNREACHABLE();
}
C4_SUPPRESS_WARNING_MSVC_POP
C4_SUPPRESS_WARNING_GCC_CLANG_POP
} // namespace yml
} // namespace c4

30
3rdparty/rapidyaml/src/c4/yml/node.cpp vendored Normal file
View File

@ -0,0 +1,30 @@
#include "c4/yml/node.hpp"
namespace c4 {
namespace yml {
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
size_t NodeRef::set_key_serialized(c4::fmt::const_base64_wrapper w)
{
_apply_seed();
csubstr encoded = this->to_arena(w);
this->set_key(encoded);
return encoded.len;
}
size_t NodeRef::set_val_serialized(c4::fmt::const_base64_wrapper w)
{
_apply_seed();
csubstr encoded = this->to_arena(w);
this->set_val(encoded);
return encoded.len;
}
} // namespace yml
} // namespace c4

5745
3rdparty/rapidyaml/src/c4/yml/parse.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,112 @@
#include "c4/yml/preprocess.hpp"
#include "c4/yml/detail/parser_dbg.hpp"
/** @file preprocess.hpp Functions for preprocessing YAML prior to parsing. */
namespace c4 {
namespace yml {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
namespace {
C4_ALWAYS_INLINE bool _is_idchar(char c)
{
return (c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9')
|| (c == '_' || c == '-' || c == '~' || c == '$');
}
typedef enum { kReadPending = 0, kKeyPending = 1, kValPending = 2 } _ppstate;
C4_ALWAYS_INLINE _ppstate _next(_ppstate s)
{
int n = (int)s + 1;
return (_ppstate)(n <= (int)kValPending ? n : 0);
}
} // empty namespace
//-----------------------------------------------------------------------------
size_t preprocess_rxmap(csubstr s, substr buf)
{
detail::_SubstrWriter writer(buf);
_ppstate state = kReadPending;
size_t last = 0;
if(s.begins_with('{'))
{
RYML_CHECK(s.ends_with('}'));
s = s.offs(1, 1);
}
writer.append('{');
for(size_t i = 0; i < s.len; ++i)
{
const char curr = s[i];
const char next = i+1 < s.len ? s[i+1] : '\0';
if(curr == '\'' || curr == '"')
{
csubstr ss = s.sub(i).pair_range_esc(curr, '\\');
i += static_cast<size_t>(ss.end() - (s.str + i));
state = _next(state);
}
else if(state == kReadPending && _is_idchar(curr))
{
state = _next(state);
}
switch(state)
{
case kKeyPending:
{
if(curr == ':' && next == ' ')
{
state = _next(state);
}
else if(curr == ',' && next == ' ')
{
writer.append(s.range(last, i));
writer.append(": 1, ");
last = i + 2;
}
break;
}
case kValPending:
{
if(curr == '[' || curr == '{' || curr == '(')
{
csubstr ss = s.sub(i).pair_range_nested(curr, '\\');
i += static_cast<size_t>(ss.end() - (s.str + i));
state = _next(state);
}
else if(curr == ',' && next == ' ')
{
state = _next(state);
}
break;
}
default:
// nothing to do
break;
}
}
writer.append(s.sub(last));
if(state == kKeyPending)
writer.append(": 1");
writer.append('}');
return writer.pos;
}
C4_SUPPRESS_WARNING_GCC_CLANG_POP
} // namespace yml
} // namespace c4

2175
3rdparty/rapidyaml/src/c4/yml/tree.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simpleini", "3rdparty\simpl
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cubeb", "3rdparty\cubeb\cubeb.vcxproj", "{BF74C473-DC04-44B3-92E8-4145F4E77342}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ryml", "3rdparty\rapidyaml\ryml.vcxproj", "{DE9653B6-17DD-356A-9EE0-28A731772587}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rapidyaml", "3rdparty\rapidyaml\rapidyaml.vcxproj", "{DE9653B6-17DD-356A-9EE0-28A731772587}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libzip", "3rdparty\libzip\libzip.vcxproj", "{20B2E9FE-F020-42A0-B324-956F5B06EA68}"
EndProject

View File

@ -77,20 +77,12 @@ endif()
set(CMAKE_FIND_FRAMEWORK ${FIND_FRAMEWORK_BACKUP})
add_subdirectory(3rdparty/rapidyaml/rapidyaml EXCLUDE_FROM_ALL)
add_subdirectory(3rdparty/fast_float EXCLUDE_FROM_ALL)
add_subdirectory(3rdparty/rapidyaml EXCLUDE_FROM_ALL)
add_subdirectory(3rdparty/lzma EXCLUDE_FROM_ALL)
add_subdirectory(3rdparty/libchdr EXCLUDE_FROM_ALL)
disable_compiler_warnings_for_target(libchdr)
add_subdirectory(3rdparty/soundtouch EXCLUDE_FROM_ALL)
# rapidyaml includes fast_float as a submodule, saves us pulling it in directly.
# Normally, we'd just pull in the cmake project, and link to it, but... it seems to enable
# permissive mode, which breaks other parts of PCSX2. So, we'll just create a target here
# for now.
#add_subdirectory(3rdparty/rapidyaml/rapidyaml/ext/c4core/src/c4/ext/fast_float EXCLUDE_FROM_ALL)
add_library(fast_float INTERFACE)
target_include_directories(fast_float INTERFACE 3rdparty/rapidyaml/rapidyaml/ext/c4core/src/c4/ext/fast_float/include)
add_subdirectory(3rdparty/simpleini EXCLUDE_FROM_ALL)
add_subdirectory(3rdparty/imgui EXCLUDE_FROM_ALL)
add_subdirectory(3rdparty/cpuinfo EXCLUDE_FROM_ALL)

View File

@ -32,7 +32,7 @@
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\rapidyaml\rapidyaml\ext\c4core\src\c4\ext\fast_float\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\fast_float\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\fmt\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\jpgd</AdditionalIncludeDirectories>
<PrecompiledHeader>Use</PrecompiledHeader>

View File

@ -37,7 +37,7 @@
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\fmt\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\imgui\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\rapidyaml\rapidyaml\ext\c4core\src\c4\ext\fast_float\include;</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\fast_float\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\simpleini\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)pcsx2</AdditionalIncludeDirectories>
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -43,7 +43,7 @@
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\imgui\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\demangler\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\cpuinfo\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\rapidyaml\rapidyaml\ext\c4core\src\c4\ext\fast_float\include;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\fast_float\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)pcsx2</AdditionalIncludeDirectories>
<!-- Needed for moc pch -->
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(ProjectDir)\Settings;$(ProjectDir)\GameList;$(ProjectDir)\Tools\InputRecording;$(ProjectDir)\Debugger;$(ProjectDir)\Debugger\Models</AdditionalIncludeDirectories>

View File

@ -1087,7 +1087,7 @@ target_link_libraries(PCSX2_FLAGS INTERFACE
common
imgui
fmt::fmt
ryml
rapidyaml::rapidyaml
libchdr
libzip::zip
cpuinfo

View File

@ -1056,7 +1056,7 @@ static bool parseHashDatabaseEntry(const c4::yml::NodeRef& node)
node["serial"] >> entry.serial;
const u32 index = static_cast<u32>(s_hash_database.size());
for (const ryml::NodeRef& n : node["hashes"].children())
for (const ryml::ConstNodeRef& n : node["hashes"].children())
{
if (!n.is_map() || !n.has_child("size") || !n.has_child("md5"))
{

View File

@ -41,9 +41,8 @@
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\simpleini\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\libzip\msvc;$(SolutionDir)3rdparty\libzip\lib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\cpuinfo\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\rapidyaml\rapidyaml\src</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\rapidyaml\rapidyaml\ext\c4core\src</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\rapidyaml\rapidyaml\ext\c4core\src\c4\ext\fast_float\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\rapidyaml\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\fast_float\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\rcheevos\include;$(SolutionDir)3rdparty\rainterface</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\soundtouch\soundtouch</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\discord-rpc\include</AdditionalIncludeDirectories>
@ -59,7 +58,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
<ForcedIncludeFiles>PrecompiledHeader.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
<PreprocessorDefinitions>ST_NO_EXCEPTION_HANDLING;ENABLE_RAINTEGRATION;ENABLE_OPENGL;ENABLE_VULKAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>C4_NO_DEBUG_BREAK;ST_NO_EXCEPTION_HANDLING;ENABLE_RAINTEGRATION;ENABLE_OPENGL;ENABLE_VULKAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Platform)'=='x64'">XBYAK_NO_EXCEPTION;ZYCORE_STATIC_DEFINE;ZYDIS_STATIC_DEFINE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ObjectFileName>$(IntDir)%(RelativeDir)</ObjectFileName>
</ClCompile>
@ -797,7 +796,7 @@
<ProjectReference Include="$(SolutionDir)3rdparty\cubeb\cubeb.vcxproj">
<Project>{bf74c473-dc04-44b3-92e8-4145f4e77342}</Project>
</ProjectReference>
<ProjectReference Include="$(SolutionDir)3rdparty\rapidyaml\ryml.vcxproj">
<ProjectReference Include="$(SolutionDir)3rdparty\rapidyaml\rapidyaml.vcxproj">
<Project>{de9653b6-17dd-356a-9ee0-28a731772587}</Project>
</ProjectReference>
<ProjectReference Include="$(SolutionDir)3rdparty\imgui\imgui.vcxproj">

View File

@ -6,6 +6,7 @@
#include "common/ProgressCallback.h"
#ifdef _WIN32
#include "common/RedtapeWindows.h"
#include "7z.h"
#include "7zFile.h"
#endif

View File

@ -9,7 +9,6 @@
#include "common/ScopedGuard.h"
#include "common/StringUtil.h"
#include "common/ProgressCallback.h"
#include "common/RedtapeWindows.h"
#include "common/RedtapeWilCom.h"
#include <CommCtrl.h>

View File

@ -34,7 +34,7 @@
<ClCompile>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\fmt\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\lzma\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\rapidyaml\rapidyaml\ext\c4core\src\c4\ext\fast_float\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\fast_float\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\wil\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>