mirror of https://github.com/PCSX2/pcsx2.git
3rdparty: Move rapidyaml v0.6.0 in-tree
Also c4core v0.2.0, fast-float v6.1.1.
This commit is contained in:
parent
d6c4a9a4d5
commit
5c59288b39
|
@ -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
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Daniel Lemire
|
||||
João Paulo Magalhaes
|
|
@ -0,0 +1,3 @@
|
|||
add_library(fast_float INTERFACE)
|
||||
target_include_directories(fast_float INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
|
|
@ -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
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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>
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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_ */
|
|
@ -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_
|
|
@ -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<*>">
|
||||
<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<*>">
|
||||
<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<*>">
|
||||
<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<char,*>">
|
||||
<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<const char,*>">
|
||||
<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<char,*>">
|
||||
<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<const char,*>">
|
||||
<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<*,*,*,*>">
|
||||
<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<*,*>">
|
||||
<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<*,*>">
|
||||
<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<*,*,*>">
|
||||
<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<*>::Sym">
|
||||
<DisplayString>{value} - {name}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[value]">value</Item>
|
||||
<Item Name="[name]">name</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="c4::EnumSymbols<*>">
|
||||
<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>
|
File diff suppressed because it is too large
Load Diff
|
@ -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_ */
|
|
@ -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_
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
File diff suppressed because it is too large
Load Diff
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_ */
|
|
@ -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_
|
|
@ -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_
|
File diff suppressed because it is too large
Load Diff
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
File diff suppressed because it is too large
Load Diff
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
File diff suppressed because it is too large
Load Diff
|
@ -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_ */
|
|
@ -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_
|
|
@ -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)
|
|
@ -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_ */
|
|
@ -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) && (anchor.len == 0)">{scalar.str,[scalar.len]}</DisplayString>
|
||||
<DisplayString Condition="(tag.len > 0) && (anchor.len == 0)">{scalar.str,[scalar.len]} [T]</DisplayString>
|
||||
<DisplayString Condition="(tag.len == 0) && (anchor.len > 0)">{scalar.str,[scalar.len]} [A]</DisplayString>
|
||||
<DisplayString Condition="(tag.len > 0) && (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 & c4::yml::VAL) != 0">c4::yml::VAL</Item>
|
||||
<Item Name="[1]" Condition="(type & c4::yml::KEY) != 0">c4::yml::KEY</Item>
|
||||
<Item Name="[2]" Condition="(type & c4::yml::MAP) != 0">c4::yml::MAP</Item>
|
||||
<Item Name="[3]" Condition="(type & c4::yml::SEQ) != 0">c4::yml::SEQ</Item>
|
||||
<Item Name="[4]" Condition="(type & c4::yml::DOC) != 0">c4::yml::DOC</Item>
|
||||
<Item Name="[5]" Condition="(type & c4::yml::STREAM) != 0">c4::yml::STREAM</Item>
|
||||
<Item Name="[6]" Condition="(type & c4::yml::KEYREF) != 0">c4::yml::KEYREF</Item>
|
||||
<Item Name="[7]" Condition="(type & c4::yml::VALREF) != 0">c4::yml::VALREF</Item>
|
||||
<Item Name="[8]" Condition="(type & c4::yml::KEYANCH) != 0">c4::yml::KEYANCH</Item>
|
||||
<Item Name="[9]" Condition="(type & c4::yml::VALANCH) != 0">c4::yml::VALANCH</Item>
|
||||
<Item Name="[10]" Condition="(type & c4::yml::KEYTAG) != 0">c4::yml::KEYTAG</Item>
|
||||
<Item Name="[11]" Condition="(type & c4::yml::VALTAG) != 0">c4::yml::VALTAG</Item>
|
||||
<Item Name="[12]" Condition="(type & c4::yml::VALQUO) != 0">c4::yml::VALQUO</Item>
|
||||
<Item Name="[13]" Condition="(type & c4::yml::KEYQUO) != 0">c4::yml::KEYQUO</Item>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="c4::yml::NodeData">
|
||||
<DisplayString Condition="((m_type.type & c4::yml::KEY ) == c4::yml::KEY) && ((m_type.type & 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 & c4::yml::KEY ) == c4::yml::KEY) && ((m_type.type & c4::yml::SEQ) == c4::yml::SEQ)">[KEYSEQ] {m_key.scalar.str,[m_key.scalar.len]}</DisplayString>
|
||||
<DisplayString Condition="((m_type.type & c4::yml::KEY ) == c4::yml::KEY) && ((m_type.type & c4::yml::MAP) == c4::yml::MAP)">[KEYMAP] {m_key.scalar.str,[m_key.scalar.len]}</DisplayString>
|
||||
<DisplayString Condition="((m_type.type & c4::yml::DOC ) == c4::yml::DOC) && ((m_type.type & c4::yml::SEQ) == c4::yml::SEQ)">[DOCSEQ]</DisplayString>
|
||||
<DisplayString Condition="((m_type.type & c4::yml::DOC ) == c4::yml::DOC) && ((m_type.type & c4::yml::MAP) == c4::yml::MAP)">[DOCMAP]</DisplayString>
|
||||
<DisplayString Condition="(m_type.type & c4::yml::VAL ) == c4::yml::VAL" >[VAL] {m_val.scalar.str,[m_val.scalar.len]}</DisplayString>
|
||||
<DisplayString Condition="(m_type.type & c4::yml::KEY ) == c4::yml::KEY" >[KEY] {m_key.scalar.str,[m_key.scalar.len]}</DisplayString>
|
||||
<DisplayString Condition="(m_type.type & c4::yml::SEQ ) == c4::yml::SEQ" >[SEQ]</DisplayString>
|
||||
<DisplayString Condition="(m_type.type & c4::yml::MAP ) == c4::yml::MAP" >[MAP]</DisplayString>
|
||||
<DisplayString Condition="(m_type.type & c4::yml::DOC ) == c4::yml::DOC" >[DOC]</DisplayString>
|
||||
<DisplayString Condition="(m_type.type & c4::yml::STREAM) == c4::yml::STREAM">[STREAM]</DisplayString>
|
||||
<DisplayString Condition="(m_type.type & c4::yml::NOTYPE) == c4::yml::NOTYPE">[NOTYPE]</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="type">m_type</Item>
|
||||
<Item Name="key" Condition="(m_type.type & c4::yml::KEY) != 0">m_key</Item>
|
||||
<Item Name="val" Condition="(m_type.type & c4::yml::VAL) != 0">m_val</Item>
|
||||
<Item Name="key quoted" Condition="((m_type.type & c4::yml::KEY) != 0) && ((m_type.type & c4::yml::KEYQUO) != 0)">c4::yml::KEYQUO</Item>
|
||||
<Item Name="val quoted" Condition="((m_type.type & c4::yml::VAL) != 0) && ((m_type.type & c4::yml::VALQUO) != 0)">c4::yml::VALQUO</Item>
|
||||
<Item Name="key ref" Condition="(m_type.type & c4::yml::KEYREF) != 0">m_key.anchor</Item>
|
||||
<Item Name="val ref" Condition="(m_type.type & c4::yml::VALREF) != 0">m_val.anchor</Item>
|
||||
<Item Name="key anchor" Condition="(m_type.type & c4::yml::KEYANCH) != 0">m_key.anchor</Item>
|
||||
<Item Name="val anchor" Condition="(m_type.type & 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) && (m_seed.str == nullptr)">[INDEX SEED for] {*(m_tree->m_buf + m_id)}</DisplayString>
|
||||
<DisplayString Condition="(m_seed.len != c4::yml::NONE) && (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) && (((m_tree->m_buf + m_id)->m_type.type & (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) && (((m_tree->m_buf + m_id)->m_type.type & (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<*,*>">
|
||||
<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>
|
|
@ -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
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1087,7 +1087,7 @@ target_link_libraries(PCSX2_FLAGS INTERFACE
|
|||
common
|
||||
imgui
|
||||
fmt::fmt
|
||||
ryml
|
||||
rapidyaml::rapidyaml
|
||||
libchdr
|
||||
libzip::zip
|
||||
cpuinfo
|
||||
|
|
|
@ -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"))
|
||||
{
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "common/ProgressCallback.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "common/RedtapeWindows.h"
|
||||
#include "7z.h"
|
||||
#include "7zFile.h"
|
||||
#endif
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue