更新:1、新增数据记录功能,将过程数据录入数据库;
2、增加输入点迹数据、凝聚后点迹数据记录; Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
This commit is contained in:
+83
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
#include <charconv>
|
||||
|
||||
#include "dragonbox_to_chars.h"
|
||||
#include "fast_float.h"
|
||||
#include "iguana/define.h"
|
||||
#include "itoa.hpp"
|
||||
|
||||
namespace iguana {
|
||||
template <typename T>
|
||||
struct is_char_type
|
||||
: std::disjunction<std::is_same<T, char>, std::is_same<T, wchar_t>,
|
||||
std::is_same<T, char16_t>, std::is_same<T, char32_t>> {};
|
||||
|
||||
inline void *to_chars_float(...) {
|
||||
throw std::runtime_error("not allowed to invoke");
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename T, typename Ret = decltype(to_chars_float(
|
||||
std::declval<T>(), std::declval<char *>()))>
|
||||
using return_of_tochars = std::conditional_t<std::is_same_v<Ret, char *>,
|
||||
std::true_type, std::false_type>;
|
||||
// here std::true_type is used as a type , any other type is also ok.
|
||||
using has_to_chars_float = iguana::return_of_tochars<std::true_type>;
|
||||
|
||||
namespace detail {
|
||||
|
||||
// check_number==true: check if the string [first, last) is a legal number
|
||||
template <bool check_number = true, typename U>
|
||||
std::pair<const char *, std::errc> from_chars(const char *first,
|
||||
const char *last, U &value) {
|
||||
using T = std::decay_t<U>;
|
||||
if constexpr (std::is_floating_point_v<T>) {
|
||||
auto [p, ec] = fast_float::from_chars(first, last, value);
|
||||
if constexpr (check_number) {
|
||||
if (p != last || ec != std::errc{})
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
|
||||
}
|
||||
return {p, ec};
|
||||
}
|
||||
else {
|
||||
auto [p, ec] = std::from_chars(first, last, value);
|
||||
if constexpr (check_number) {
|
||||
if (p != last || ec != std::errc{})
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
|
||||
}
|
||||
return {p, ec};
|
||||
}
|
||||
}
|
||||
|
||||
// not support uint8 for now
|
||||
template <typename T>
|
||||
char *to_chars(char *buffer, T value) noexcept {
|
||||
using U = std::decay_t<T>;
|
||||
if constexpr (std::is_floating_point_v<U>) {
|
||||
if constexpr (has_to_chars_float::value) {
|
||||
return static_cast<char *>(to_chars_float(value, buffer));
|
||||
}
|
||||
else {
|
||||
return jkj::dragonbox::to_chars(value, buffer);
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_signed_v<U> && (sizeof(U) >= 8)) {
|
||||
return xtoa(value, buffer, 10, 1); // int64_t
|
||||
}
|
||||
else if constexpr (std::is_unsigned_v<U> && (sizeof(U) >= 8)) {
|
||||
return xtoa(value, buffer, 10, 0); // uint64_t
|
||||
}
|
||||
else if constexpr (std::is_integral_v<U> && (sizeof(U) > 1)) {
|
||||
return itoa_fwd(value, buffer); // only support more than 2 bytes intergal
|
||||
}
|
||||
else if constexpr (!is_char_type<U>::value) {
|
||||
return itoa_fwd(static_cast<int>(value),
|
||||
buffer); // only support more than 2 bytes intergal
|
||||
}
|
||||
else {
|
||||
static_assert(!sizeof(U), "only support arithmetic type except char type");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace iguana
|
||||
+3044
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,629 @@
|
||||
// Copyright 2020-2022 Junekey Jeon
|
||||
//
|
||||
// The contents of this file may be used under the terms of
|
||||
// the Apache License v2.0 with LLVM Exceptions.
|
||||
//
|
||||
// (See accompanying file LICENSE-Apache or copy at
|
||||
// https://llvm.org/foundation/relicensing/LICENSE.txt)
|
||||
//
|
||||
// Alternatively, the contents of this file may be used under the terms of
|
||||
// the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE-Boost or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, this software
|
||||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied.
|
||||
|
||||
#ifndef JKJ_HEADER_DRAGONBOX_TO_CHARS
|
||||
#define JKJ_HEADER_DRAGONBOX_TO_CHARS
|
||||
|
||||
#include "dragonbox.h"
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define JKJ_FORCEINLINE inline __attribute__((always_inline))
|
||||
#elif defined(_MSC_VER)
|
||||
#define JKJ_FORCEINLINE __forceinline
|
||||
#else
|
||||
#define JKJ_FORCEINLINE inline
|
||||
#endif
|
||||
|
||||
namespace jkj::dragonbox {
|
||||
namespace to_chars_detail {
|
||||
template <class Float, class FloatTraits>
|
||||
extern char *to_chars(typename FloatTraits::carrier_uint significand,
|
||||
int exponent, char *buffer) noexcept;
|
||||
|
||||
// These "//"'s are to prevent clang-format to ruin this nice alignment.
|
||||
// Thanks to reddit user u/mcmcc:
|
||||
// https://www.reddit.com/r/cpp/comments/so3wx9/dragonbox_110_is_released_a_fast_floattostring/hw8z26r/?context=3
|
||||
static constexpr char radix_100_table[] = {
|
||||
'0', '0', '0', '1', '0', '2', '0', '3', '0', '4', //
|
||||
'0', '5', '0', '6', '0', '7', '0', '8', '0', '9', //
|
||||
'1', '0', '1', '1', '1', '2', '1', '3', '1', '4', //
|
||||
'1', '5', '1', '6', '1', '7', '1', '8', '1', '9', //
|
||||
'2', '0', '2', '1', '2', '2', '2', '3', '2', '4', //
|
||||
'2', '5', '2', '6', '2', '7', '2', '8', '2', '9', //
|
||||
'3', '0', '3', '1', '3', '2', '3', '3', '3', '4', //
|
||||
'3', '5', '3', '6', '3', '7', '3', '8', '3', '9', //
|
||||
'4', '0', '4', '1', '4', '2', '4', '3', '4', '4', //
|
||||
'4', '5', '4', '6', '4', '7', '4', '8', '4', '9', //
|
||||
'5', '0', '5', '1', '5', '2', '5', '3', '5', '4', //
|
||||
'5', '5', '5', '6', '5', '7', '5', '8', '5', '9', //
|
||||
'6', '0', '6', '1', '6', '2', '6', '3', '6', '4', //
|
||||
'6', '5', '6', '6', '6', '7', '6', '8', '6', '9', //
|
||||
'7', '0', '7', '1', '7', '2', '7', '3', '7', '4', //
|
||||
'7', '5', '7', '6', '7', '7', '7', '8', '7', '9', //
|
||||
'8', '0', '8', '1', '8', '2', '8', '3', '8', '4', //
|
||||
'8', '5', '8', '6', '8', '7', '8', '8', '8', '9', //
|
||||
'9', '0', '9', '1', '9', '2', '9', '3', '9', '4', //
|
||||
'9', '5', '9', '6', '9', '7', '9', '8', '9', '9' //
|
||||
};
|
||||
static constexpr char radix_100_head_table[] = {
|
||||
'0', '.', '1', '.', '2', '.', '3', '.', '4', '.', //
|
||||
'5', '.', '6', '.', '7', '.', '8', '.', '9', '.', //
|
||||
'1', '.', '1', '.', '1', '.', '1', '.', '1', '.', //
|
||||
'1', '.', '1', '.', '1', '.', '1', '.', '1', '.', //
|
||||
'2', '.', '2', '.', '2', '.', '2', '.', '2', '.', //
|
||||
'2', '.', '2', '.', '2', '.', '2', '.', '2', '.', //
|
||||
'3', '.', '3', '.', '3', '.', '3', '.', '3', '.', //
|
||||
'3', '.', '3', '.', '3', '.', '3', '.', '3', '.', //
|
||||
'4', '.', '4', '.', '4', '.', '4', '.', '4', '.', //
|
||||
'4', '.', '4', '.', '4', '.', '4', '.', '4', '.', //
|
||||
'5', '.', '5', '.', '5', '.', '5', '.', '5', '.', //
|
||||
'5', '.', '5', '.', '5', '.', '5', '.', '5', '.', //
|
||||
'6', '.', '6', '.', '6', '.', '6', '.', '6', '.', //
|
||||
'6', '.', '6', '.', '6', '.', '6', '.', '6', '.', //
|
||||
'7', '.', '7', '.', '7', '.', '7', '.', '7', '.', //
|
||||
'7', '.', '7', '.', '7', '.', '7', '.', '7', '.', //
|
||||
'8', '.', '8', '.', '8', '.', '8', '.', '8', '.', //
|
||||
'8', '.', '8', '.', '8', '.', '8', '.', '8', '.', //
|
||||
'9', '.', '9', '.', '9', '.', '9', '.', '9', '.', //
|
||||
'9', '.', '9', '.', '9', '.', '9', '.', '9', '.' //
|
||||
};
|
||||
|
||||
// These digit generation routines are inspired by James Anhalt's itoa
|
||||
// algorithm: https://github.com/jeaiii/itoa The main idea is for given n, find
|
||||
// y such that floor(10^k * y / 2^32) = n holds, where k is an appropriate
|
||||
// integer depending on the length of n. For example, if n = 1234567, we set k
|
||||
// = 6. In this case, we have floor(y / 2^32) = 1, floor(10^2 * ((10^0 * y) mod
|
||||
// 2^32) / 2^32) = 23, floor(10^2 * ((10^2 * y) mod 2^32) / 2^32) = 45, and
|
||||
// floor(10^2 * ((10^4 * y) mod 2^32) / 2^32) = 67.
|
||||
// See https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ for more
|
||||
// explanation.
|
||||
|
||||
JKJ_FORCEINLINE static void print_9_digits(std::uint32_t s32, int &exponent,
|
||||
char *&buffer) noexcept {
|
||||
// -- IEEE-754 binary32
|
||||
// Since we do not cut trailing zeros in advance, s32 must be of 6~9 digits
|
||||
// unless the original input was subnormal.
|
||||
// In particular, when it is of 9 digits it shouldn't have any trailing zeros.
|
||||
// -- IEEE-754 binary64
|
||||
// In this case, s32 must be of 7~9 digits unless the input is subnormal,
|
||||
// and it shouldn't have any trailing zeros if it is of 9 digits.
|
||||
if (s32 >= 1'0000'0000) {
|
||||
// 9 digits.
|
||||
// 1441151882 = ceil(2^57 / 1'0000'0000) + 1
|
||||
auto prod = s32 * std::uint64_t(1441151882);
|
||||
prod >>= 25;
|
||||
std::memcpy(buffer, radix_100_head_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2, 2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 4, radix_100_table + std::uint32_t(prod >> 32) * 2, 2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 6, radix_100_table + std::uint32_t(prod >> 32) * 2, 2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 8, radix_100_table + std::uint32_t(prod >> 32) * 2, 2);
|
||||
|
||||
exponent += 8;
|
||||
buffer += 10;
|
||||
}
|
||||
else if (s32 >= 100'0000) {
|
||||
// 7 or 8 digits.
|
||||
// 281474978 = ceil(2^48 / 100'0000) + 1
|
||||
auto prod = s32 * std::uint64_t(281474978);
|
||||
prod >>= 16;
|
||||
auto two_digits = std::uint32_t(prod >> 32);
|
||||
// If s32 is of 8 digits, increase the exponent by 7.
|
||||
// Otherwise, increase it by 6.
|
||||
exponent += (6 + unsigned(two_digits >= 10));
|
||||
|
||||
// Write the first digit and the decimal point.
|
||||
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
|
||||
// This third character may be overwritten later but we don't care.
|
||||
buffer[2] = radix_100_table[two_digits * 2 + 1];
|
||||
|
||||
// Remaining 6 digits are all zero?
|
||||
if (std::uint32_t(prod) <=
|
||||
std::uint32_t((std::uint64_t(1) << 32) / 100'0000)) {
|
||||
// The number of characters actually written is:
|
||||
// 1, if only the first digit is nonzero, which means that either s32 is
|
||||
// of 7 digits or it is of 8 digits but the second digit is zero, or 3,
|
||||
// otherwise.
|
||||
// Note that buffer[2] is never zero if s32 is of 7 digits, because the
|
||||
// input is never zero.
|
||||
buffer +=
|
||||
(1 + (unsigned(two_digits >= 10) & unsigned(buffer[2] > '0')) * 2);
|
||||
}
|
||||
else {
|
||||
// At least one of the remaining 6 digits are nonzero.
|
||||
// After this adjustment, now the first destination becomes buffer + 2.
|
||||
buffer += unsigned(two_digits >= 10);
|
||||
|
||||
// Obtain the next two digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer + 2, radix_100_table + two_digits * 2, 2);
|
||||
|
||||
// Remaining 4 digits are all zero?
|
||||
if (std::uint32_t(prod) <=
|
||||
std::uint32_t((std::uint64_t(1) << 32) / 1'0000)) {
|
||||
buffer += (3 + unsigned(buffer[3] > '0'));
|
||||
}
|
||||
else {
|
||||
// At least one of the remaining 4 digits are nonzero.
|
||||
|
||||
// Obtain the next two digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer + 4, radix_100_table + two_digits * 2, 2);
|
||||
|
||||
// Remaining 2 digits are all zero?
|
||||
if (std::uint32_t(prod) <=
|
||||
std::uint32_t((std::uint64_t(1) << 32) / 100)) {
|
||||
buffer += (5 + unsigned(buffer[5] > '0'));
|
||||
}
|
||||
else {
|
||||
// Obtain the last two digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer + 6, radix_100_table + two_digits * 2, 2);
|
||||
|
||||
buffer += (7 + unsigned(buffer[7] > '0'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (s32 >= 1'0000) {
|
||||
// 5 or 6 digits.
|
||||
// 429497 = ceil(2^32 / 1'0000)
|
||||
auto prod = s32 * std::uint64_t(429497);
|
||||
auto two_digits = std::uint32_t(prod >> 32);
|
||||
|
||||
// If s32 is of 6 digits, increase the exponent by 5.
|
||||
// Otherwise, increase it by 4.
|
||||
exponent += (4 + unsigned(two_digits >= 10));
|
||||
|
||||
// Write the first digit and the decimal point.
|
||||
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
|
||||
// This third character may be overwritten later but we don't care.
|
||||
buffer[2] = radix_100_table[two_digits * 2 + 1];
|
||||
|
||||
// Remaining 4 digits are all zero?
|
||||
if (std::uint32_t(prod) <=
|
||||
std::uint32_t((std::uint64_t(1) << 32) / 1'0000)) {
|
||||
// The number of characters actually written is 1 or 3, similarly to the
|
||||
// case of 7 or 8 digits.
|
||||
buffer +=
|
||||
(1 + (unsigned(two_digits >= 10) & unsigned(buffer[2] > '0')) * 2);
|
||||
}
|
||||
else {
|
||||
// At least one of the remaining 4 digits are nonzero.
|
||||
// After this adjustment, now the first destination becomes buffer + 2.
|
||||
buffer += unsigned(two_digits >= 10);
|
||||
|
||||
// Obtain the next two digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer + 2, radix_100_table + two_digits * 2, 2);
|
||||
|
||||
// Remaining 2 digits are all zero?
|
||||
if (std::uint32_t(prod) <=
|
||||
std::uint32_t((std::uint64_t(1) << 32) / 100)) {
|
||||
buffer += (3 + unsigned(buffer[3] > '0'));
|
||||
}
|
||||
else {
|
||||
// Obtain the last two digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer + 4, radix_100_table + two_digits * 2, 2);
|
||||
|
||||
buffer += (5 + unsigned(buffer[5] > '0'));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (s32 >= 100) {
|
||||
// 3 or 4 digits.
|
||||
// 42949673 = ceil(2^32 / 100)
|
||||
auto prod = s32 * std::uint64_t(42949673);
|
||||
auto two_digits = std::uint32_t(prod >> 32);
|
||||
|
||||
// If s32 is of 4 digits, increase the exponent by 3.
|
||||
// Otherwise, increase it by 2.
|
||||
exponent += (2 + int(two_digits >= 10));
|
||||
|
||||
// Write the first digit and the decimal point.
|
||||
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
|
||||
// This third character may be overwritten later but we don't care.
|
||||
buffer[2] = radix_100_table[two_digits * 2 + 1];
|
||||
|
||||
// Remaining 2 digits are all zero?
|
||||
if (std::uint32_t(prod) <= std::uint32_t((std::uint64_t(1) << 32) / 100)) {
|
||||
// The number of characters actually written is 1 or 3, similarly to the
|
||||
// case of 7 or 8 digits.
|
||||
buffer +=
|
||||
(1 + (unsigned(two_digits >= 10) & unsigned(buffer[2] > '0')) * 2);
|
||||
}
|
||||
else {
|
||||
// At least one of the remaining 2 digits are nonzero.
|
||||
// After this adjustment, now the first destination becomes buffer + 2.
|
||||
buffer += unsigned(two_digits >= 10);
|
||||
|
||||
// Obtain the last two digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer + 2, radix_100_table + two_digits * 2, 2);
|
||||
|
||||
buffer += (3 + unsigned(buffer[3] > '0'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 1 or 2 digits.
|
||||
// If s32 is of 2 digits, increase the exponent by 1.
|
||||
exponent += int(s32 >= 10);
|
||||
|
||||
// Write the first digit and the decimal point.
|
||||
std::memcpy(buffer, radix_100_head_table + s32 * 2, 2);
|
||||
// This third character may be overwritten later but we don't care.
|
||||
buffer[2] = radix_100_table[s32 * 2 + 1];
|
||||
|
||||
// The number of characters actually written is 1 or 3, similarly to the
|
||||
// case of 7 or 8 digits.
|
||||
buffer += (1 + (unsigned(s32 >= 10) & unsigned(buffer[2] > '0')) * 2);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
inline char *to_chars<float, default_float_traits<float>>(
|
||||
std::uint32_t s32, int exponent, char *buffer) noexcept {
|
||||
// Print significand.
|
||||
print_9_digits(s32, exponent, buffer);
|
||||
|
||||
// Print exponent and return
|
||||
if (exponent < 0) {
|
||||
std::memcpy(buffer, "E-", 2);
|
||||
buffer += 2;
|
||||
exponent = -exponent;
|
||||
}
|
||||
else {
|
||||
buffer[0] = 'E';
|
||||
buffer += 1;
|
||||
}
|
||||
|
||||
if (exponent >= 10) {
|
||||
std::memcpy(buffer, &radix_100_table[exponent * 2], 2);
|
||||
buffer += 2;
|
||||
}
|
||||
else {
|
||||
buffer[0] = char('0' + exponent);
|
||||
buffer += 1;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline char *to_chars<double, default_float_traits<double>>(
|
||||
std::uint64_t const significand, int exponent, char *buffer) noexcept {
|
||||
// Print significand by decomposing it into a 9-digit block and a 8-digit
|
||||
// block.
|
||||
std::uint32_t first_block, second_block;
|
||||
bool no_second_block;
|
||||
|
||||
if (significand >= 1'0000'0000) {
|
||||
first_block = std::uint32_t(significand / 1'0000'0000);
|
||||
second_block = std::uint32_t(significand) - first_block * 1'0000'0000;
|
||||
exponent += 8;
|
||||
no_second_block = (second_block == 0);
|
||||
}
|
||||
else {
|
||||
first_block = std::uint32_t(significand);
|
||||
no_second_block = true;
|
||||
}
|
||||
|
||||
if (no_second_block) {
|
||||
print_9_digits(first_block, exponent, buffer);
|
||||
}
|
||||
else {
|
||||
// We proceed similarly to print_9_digits(), but since we do not need to
|
||||
// remove trailing zeros, the procedure is a bit simpler.
|
||||
if (first_block >= 1'0000'0000) {
|
||||
// The input is of 17 digits, thus there should be no trailing zero at
|
||||
// all. The first block is of 9 digits. 1441151882 = ceil(2^57 /
|
||||
// 1'0000'0000) + 1
|
||||
auto prod = first_block * std::uint64_t(1441151882);
|
||||
prod >>= 25;
|
||||
std::memcpy(buffer, radix_100_head_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 4, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 6, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 8, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
|
||||
// The second block is of 8 digits.
|
||||
// 281474978 = ceil(2^48 / 100'0000) + 1
|
||||
prod = second_block * std::uint64_t(281474978);
|
||||
prod >>= 16;
|
||||
prod += 1;
|
||||
std::memcpy(buffer + 10, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 12, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 14, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 16, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
|
||||
exponent += 8;
|
||||
buffer += 18;
|
||||
}
|
||||
else {
|
||||
if (first_block >= 100'0000) {
|
||||
// 7 or 8 digits.
|
||||
// 281474978 = ceil(2^48 / 100'0000) + 1
|
||||
auto prod = first_block * std::uint64_t(281474978);
|
||||
prod >>= 16;
|
||||
auto two_digits = std::uint32_t(prod >> 32);
|
||||
|
||||
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
|
||||
buffer[2] = radix_100_table[two_digits * 2 + 1];
|
||||
|
||||
exponent += (6 + unsigned(two_digits >= 10));
|
||||
buffer += unsigned(two_digits >= 10);
|
||||
|
||||
// Print remaining 6 digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 4, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 6, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
|
||||
buffer += 8;
|
||||
}
|
||||
else if (first_block >= 1'0000) {
|
||||
// 5 or 6 digits.
|
||||
// 429497 = ceil(2^32 / 1'0000)
|
||||
auto prod = first_block * std::uint64_t(429497);
|
||||
auto two_digits = std::uint32_t(prod >> 32);
|
||||
|
||||
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
|
||||
buffer[2] = radix_100_table[two_digits * 2 + 1];
|
||||
|
||||
exponent += (4 + unsigned(two_digits >= 10));
|
||||
buffer += unsigned(two_digits >= 10);
|
||||
|
||||
// Print remaining 4 digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 4, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
|
||||
buffer += 6;
|
||||
}
|
||||
else if (first_block >= 100) {
|
||||
// 3 or 4 digits.
|
||||
// 42949673 = ceil(2^32 / 100)
|
||||
auto prod = first_block * std::uint64_t(42949673);
|
||||
auto two_digits = std::uint32_t(prod >> 32);
|
||||
|
||||
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
|
||||
buffer[2] = radix_100_table[two_digits * 2 + 1];
|
||||
|
||||
exponent += (2 + unsigned(two_digits >= 10));
|
||||
buffer += unsigned(two_digits >= 10);
|
||||
|
||||
// Print remaining 2 digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
|
||||
buffer += 4;
|
||||
}
|
||||
else {
|
||||
// 1 or 2 digits.
|
||||
std::memcpy(buffer, radix_100_head_table + first_block * 2, 2);
|
||||
buffer[2] = radix_100_table[first_block * 2 + 1];
|
||||
|
||||
exponent += unsigned(first_block >= 10);
|
||||
buffer += (2 + unsigned(first_block >= 10));
|
||||
}
|
||||
|
||||
// Next, print the second block.
|
||||
// The second block is of 8 digits, but we may have trailing zeros.
|
||||
// 281474978 = ceil(2^48 / 100'0000) + 1
|
||||
auto prod = second_block * std::uint64_t(281474978);
|
||||
prod >>= 16;
|
||||
prod += 1;
|
||||
auto two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer, radix_100_table + two_digits * 2, 2);
|
||||
|
||||
// Remaining 6 digits are all zero?
|
||||
if (std::uint32_t(prod) <=
|
||||
std::uint32_t((std::uint64_t(1) << 32) / 100'0000)) {
|
||||
buffer += (1 + unsigned(buffer[1] > '0'));
|
||||
}
|
||||
else {
|
||||
// Obtain the next two digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer + 2, radix_100_table + two_digits * 2, 2);
|
||||
|
||||
// Remaining 4 digits are all zero?
|
||||
if (std::uint32_t(prod) <=
|
||||
std::uint32_t((std::uint64_t(1) << 32) / 1'0000)) {
|
||||
buffer += (3 + unsigned(buffer[3] > '0'));
|
||||
}
|
||||
else {
|
||||
// Obtain the next two digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer + 4, radix_100_table + two_digits * 2, 2);
|
||||
|
||||
// Remaining 2 digits are all zero?
|
||||
if (std::uint32_t(prod) <=
|
||||
std::uint32_t((std::uint64_t(1) << 32) / 100)) {
|
||||
buffer += (5 + unsigned(buffer[5] > '0'));
|
||||
}
|
||||
else {
|
||||
// Obtain the last two digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer + 6, radix_100_table + two_digits * 2, 2);
|
||||
buffer += (7 + unsigned(buffer[7] > '0'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print exponent and return
|
||||
if (exponent < 0) {
|
||||
std::memcpy(buffer, "E-", 2);
|
||||
buffer += 2;
|
||||
exponent = -exponent;
|
||||
}
|
||||
else {
|
||||
buffer[0] = 'E';
|
||||
buffer += 1;
|
||||
}
|
||||
|
||||
if (exponent >= 100) {
|
||||
// d1 = exponent / 10; d2 = exponent % 10;
|
||||
// 6554 = ceil(2^16 / 10)
|
||||
auto prod = std::uint32_t(exponent) * std::uint32_t(6554);
|
||||
auto d1 = prod >> 16;
|
||||
prod = std::uint16_t(prod) * std::uint32_t(5); // * 10
|
||||
auto d2 = prod >> 15; // >> 16
|
||||
std::memcpy(buffer, &radix_100_table[d1 * 2], 2);
|
||||
buffer[2] = char('0' + d2);
|
||||
buffer += 3;
|
||||
}
|
||||
else if (exponent >= 10) {
|
||||
std::memcpy(buffer, &radix_100_table[exponent * 2], 2);
|
||||
buffer += 2;
|
||||
}
|
||||
else {
|
||||
buffer[0] = char('0' + exponent);
|
||||
buffer += 1;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Avoid needless ABI overhead incurred by tag dispatch.
|
||||
template <class PolicyHolder, class Float, class FloatTraits>
|
||||
char *to_chars_n_impl(float_bits<Float, FloatTraits> br,
|
||||
char *buffer) noexcept {
|
||||
auto const exponent_bits = br.extract_exponent_bits();
|
||||
auto const s = br.remove_exponent_bits(exponent_bits);
|
||||
|
||||
if (br.is_finite(exponent_bits)) {
|
||||
if (s.is_negative()) {
|
||||
*buffer = '-';
|
||||
++buffer;
|
||||
}
|
||||
if (br.is_nonzero()) {
|
||||
auto result = to_decimal<Float, FloatTraits>(
|
||||
s, exponent_bits, policy::sign::ignore, policy::trailing_zero::ignore,
|
||||
typename PolicyHolder::decimal_to_binary_rounding_policy{},
|
||||
typename PolicyHolder::binary_to_decimal_rounding_policy{},
|
||||
typename PolicyHolder::cache_policy{});
|
||||
return to_chars_detail::to_chars<Float, FloatTraits>(
|
||||
result.significand, result.exponent, buffer);
|
||||
}
|
||||
else {
|
||||
std::memcpy(buffer, "0E0", 3);
|
||||
return buffer + 3;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (s.has_all_zero_significand_bits()) {
|
||||
if (s.is_negative()) {
|
||||
*buffer = '-';
|
||||
++buffer;
|
||||
}
|
||||
std::memcpy(buffer, "Infinity", 8);
|
||||
return buffer + 8;
|
||||
}
|
||||
else {
|
||||
std::memcpy(buffer, "NaN", 3);
|
||||
return buffer + 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace to_chars_detail
|
||||
|
||||
// Returns the next-to-end position
|
||||
template <class Float, class FloatTraits = default_float_traits<Float>,
|
||||
class... Policies>
|
||||
char *to_chars_n(Float x, char *buffer, Policies... policies) noexcept {
|
||||
using namespace jkj::dragonbox::detail::policy_impl;
|
||||
using policy_holder = decltype(make_policy_holder(
|
||||
base_default_pair_list<
|
||||
base_default_pair<decimal_to_binary_rounding::base,
|
||||
decimal_to_binary_rounding::nearest_to_even>,
|
||||
base_default_pair<binary_to_decimal_rounding::base,
|
||||
binary_to_decimal_rounding::to_even>,
|
||||
base_default_pair<cache::base, cache::full>>{},
|
||||
policies...));
|
||||
|
||||
return to_chars_detail::to_chars_n_impl<policy_holder>(
|
||||
float_bits<Float, FloatTraits>(x), buffer);
|
||||
}
|
||||
|
||||
// Null-terminate and bypass the return value of fp_to_chars_n
|
||||
template <class Float, class FloatTraits = default_float_traits<Float>,
|
||||
class... Policies>
|
||||
char *to_chars(Float x, char *buffer, Policies... policies) noexcept {
|
||||
auto ptr = to_chars_n<Float, FloatTraits>(x, buffer, policies...);
|
||||
*ptr = '\0';
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Maximum required buffer size (excluding null-terminator)
|
||||
template <class FloatFormat>
|
||||
inline constexpr std::size_t max_output_string_length =
|
||||
std::is_same_v<FloatFormat, ieee754_binary32>
|
||||
?
|
||||
// sign(1) + significand(9) + decimal_point(1) + exp_marker(1) +
|
||||
// exp_sign(1) + exp(2)
|
||||
(1 + 9 + 1 + 1 + 1 + 2)
|
||||
:
|
||||
// format == ieee754_format::binary64
|
||||
// sign(1) + significand(17) + decimal_point(1) + exp_marker(1) +
|
||||
// exp_sign(1) + exp(3)
|
||||
(1 + 17 + 1 + 1 + 1 + 3);
|
||||
} // namespace jkj::dragonbox
|
||||
|
||||
#endif
|
||||
+3196
File diff suppressed because it is too large
Load Diff
+321
@@ -0,0 +1,321 @@
|
||||
//=== itoa.h - Fast integer to ascii conversion --*- C++ -*-//
|
||||
//
|
||||
// The MIT License (MIT)
|
||||
// Copyright (c) 2016 Arturo Martin-de-Nicolas
|
||||
//
|
||||
// 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.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef DEC_ITOA_IMPL_H
|
||||
#define DEC_ITOA_IMPL_H
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring> // memcpy
|
||||
#include <type_traits>
|
||||
|
||||
namespace dec_ {
|
||||
|
||||
// Using a lookup table to convert binary numbers from 0 to 99
|
||||
// into ascii characters as described by Andrei Alexandrescu in
|
||||
// https://www.facebook.com/notes/facebook-engineering/
|
||||
// three-optimization-tips-for-c/10151361643253920/
|
||||
|
||||
template <typename T, size_t N, typename Gen, size_t... Is>
|
||||
constexpr auto generate_array(Gen&& item, std::index_sequence<Is...>) {
|
||||
return std::array<T, N>{{item(Is)...}};
|
||||
}
|
||||
|
||||
const std::array<char, 200> digits = generate_array<char, 200>(
|
||||
[](size_t i) {
|
||||
return char('0' + ((i % 2) ? ((i / 2) % 10) : ((i / 2) / 10)));
|
||||
},
|
||||
std::make_index_sequence<200>{});
|
||||
|
||||
// extern const std::array<char,200> digits;
|
||||
static inline uint16_t const& dd(uint8_t u) {
|
||||
return reinterpret_cast<uint16_t const*>(digits.data())[u];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static constexpr T pow10(size_t x) {
|
||||
return x ? 10 * pow10<T>(x - 1) : 1;
|
||||
}
|
||||
|
||||
// Division by a power of 10 is implemented using a multiplicative inverse.
|
||||
// This strength reduction is also done by optimizing compilers, but
|
||||
// presently the fastest results are produced by using the values
|
||||
// for the multiplication and the shift as given by the algorithm
|
||||
// described by Agner Fog in "Optimizing Subroutines in Assembly Language"
|
||||
//
|
||||
// http://www.agner.org/optimize/optimizing_assembly.pdf
|
||||
//
|
||||
// "Integer division by a constant (all processors)
|
||||
// A floating point number can be divided by a constant by multiplying
|
||||
// with the reciprocal. If we want to do the same with integers, we have
|
||||
// to scale the reciprocal by 2n and then shift the product to the right
|
||||
// by n. There are various algorithms for finding a suitable value of n
|
||||
// and compensating for rounding errors. The algorithm described below
|
||||
// was invented by Terje Mathisen, Norway, and not published elsewhere."
|
||||
|
||||
// using uint128_t = unsigned __int128;
|
||||
|
||||
template <typename UInt, bool A, UInt M, unsigned S>
|
||||
struct MulInv {
|
||||
using type = UInt;
|
||||
static constexpr bool a{A};
|
||||
static constexpr UInt m{M};
|
||||
static constexpr unsigned s{S};
|
||||
};
|
||||
template <int, int, class...>
|
||||
struct UT;
|
||||
template <int N, class T, class... Ts>
|
||||
struct UT<N, N, T, Ts...> {
|
||||
using U = T;
|
||||
};
|
||||
template <int N, int M, class T, class... Ts>
|
||||
struct UT<N, M, T, Ts...> {
|
||||
using U = typename UT<N, 2 * M, Ts...>::U;
|
||||
};
|
||||
template <int N>
|
||||
using MI = typename UT<N, 1, MulInv<uint8_t, 0, 205U, 11>,
|
||||
MulInv<uint16_t, 1, 41943U, 22>,
|
||||
MulInv<uint32_t, 0, 3518437209U, 45>,
|
||||
MulInv<uint64_t, 0, 12379400392853802749U, 90>>::U;
|
||||
template <int N>
|
||||
using U = typename MI<N>::type;
|
||||
|
||||
// struct QR holds the result of dividing an unsigned N-byte variable
|
||||
// by 10^N resulting in
|
||||
template <size_t N>
|
||||
struct QR {
|
||||
U<N> q; // quotient with fewer than 2*N decimal digits
|
||||
U<N / 2> r; // remainder with at most N decimal digits
|
||||
};
|
||||
template <size_t N>
|
||||
QR<N> static inline split(U<N> u) {
|
||||
constexpr MI<N> mi{};
|
||||
U<N> q = (mi.m * (U<2 * N>(u) + mi.a)) >> mi.s;
|
||||
return {q, U<N / 2>(u - q * pow10<U<N / 2>>(N))};
|
||||
}
|
||||
|
||||
enum Direction { Fwd, Rev };
|
||||
|
||||
template <Direction D>
|
||||
struct convert {
|
||||
//===----------------------------------------------------------===//
|
||||
// output the digits in either a forward or reverse direction
|
||||
// use memcpy so compiler handles alignment on target architecture.
|
||||
// Typically generates one store to memory with an optimizing
|
||||
// compiler for target architecture that supports unaligned access.
|
||||
//===----------------------------------------------------------===//
|
||||
|
||||
template <typename T>
|
||||
static inline char* out(char* p, T&& obj) {
|
||||
if (D == Rev)
|
||||
p -= sizeof(T);
|
||||
memcpy(p, reinterpret_cast<const void*>(&obj), sizeof(T));
|
||||
if (D == Fwd)
|
||||
p += sizeof(T);
|
||||
return p;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------===//
|
||||
// head: find most significant digit, skip leading zeros
|
||||
//===----------------------------------------------------------===//
|
||||
|
||||
// "x" contains quotient and remainder after division by 10^N
|
||||
// quotient is less than 10^N
|
||||
template <size_t N>
|
||||
static inline char* head(char* p, QR<N> x) {
|
||||
return (D == Fwd ? (tail(head(p, U<N / 2>(x.q)), x.r))
|
||||
: (head(tail(p, x.r), U<N / 2>(x.q))));
|
||||
}
|
||||
|
||||
// "u" is less than 10^2*N
|
||||
template <typename UInt, size_t N = sizeof(UInt)>
|
||||
static inline char* head(char* p, UInt u) {
|
||||
return (u < pow10<U<N>>(N) ? (head(p, U<N / 2>(u)))
|
||||
: (head<N>(p, split<N>(u))));
|
||||
}
|
||||
|
||||
// recursion base case, selected when "u" is one byte
|
||||
static inline char* head(char* p, U<1> u) {
|
||||
return (u < 10 ? (out<char>(p, '0' + u)) : (out(p, dd(u))));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------===//
|
||||
// tail: produce all digits including leading zeros
|
||||
//===----------------------------------------------------------===//
|
||||
|
||||
// recursive step, "u" is less than 10^2*N
|
||||
template <typename UInt, size_t N = sizeof(UInt)>
|
||||
static inline char* tail(char* p, UInt u) {
|
||||
QR<N> x = split<N>(u);
|
||||
return (D == Fwd ? (tail(tail(p, U<N / 2>(x.q)), x.r))
|
||||
: (tail(tail(p, x.r), U<N / 2>(x.q))));
|
||||
}
|
||||
|
||||
// recursion base case, selected when "u" is one byte
|
||||
static inline char* tail(char* p, U<1> u) { return out(p, dd(u)); }
|
||||
|
||||
//===----------------------------------------------------------===//
|
||||
// large values are >= 10^2*N
|
||||
// where x contains quotient and remainder after division by 10^N
|
||||
//===----------------------------------------------------------===//
|
||||
|
||||
template <size_t N>
|
||||
static inline char* large(char* p, QR<N> x) {
|
||||
QR<N> y = split<N>(x.q);
|
||||
return (D == Fwd ? (tail(tail(head(p, U<N / 2>(y.q)), y.r), x.r))
|
||||
: (head(tail(tail(p, x.r), y.r), U<N / 2>(y.q))));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------===//
|
||||
// handle values of "u" that might be >= 10^2*N
|
||||
// where N is the size of "u" in bytes
|
||||
//===----------------------------------------------------------===//
|
||||
|
||||
template <typename UInt, size_t N = sizeof(UInt)>
|
||||
static inline char* itoa(char* p, UInt u) {
|
||||
if (u < pow10<U<N>>(N))
|
||||
return head(p, U<N / 2>(u));
|
||||
QR<N> x = split<N>(u);
|
||||
return (u < pow10<U<N>>(2 * N) ? (head<N>(p, x)) : (large<N>(p, x)));
|
||||
}
|
||||
|
||||
// selected when "u" is one byte
|
||||
static inline char* itoa(char* p, U<1> u) {
|
||||
if (u < 10)
|
||||
return out<char>(p, '0' + u);
|
||||
if (u < 100)
|
||||
return out(p, dd(u));
|
||||
return (D == Fwd ? (out(out<char>(p, '0' + u / 100), dd(u % 100)))
|
||||
: (out<char>(out(p, dd(u % 100)), '0' + u / 100)));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------===//
|
||||
// handle unsigned and signed integral operands
|
||||
//===----------------------------------------------------------===//
|
||||
|
||||
// itoa: handle unsigned integral operands (selected by SFINAE)
|
||||
template <typename U, std::enable_if_t<!std::is_signed<U>::value &&
|
||||
std::is_integral<U>::value>* = nullptr>
|
||||
static inline char* itoa(U u, char* p) {
|
||||
return convert<D>::template itoa<>(p, u);
|
||||
}
|
||||
|
||||
// itoa: handle signed integral operands (selected by SFINAE)
|
||||
template <typename I, size_t N = sizeof(I),
|
||||
std::enable_if_t<std::is_signed<I>::value &&
|
||||
std::is_integral<I>::value>* = nullptr>
|
||||
static inline char* itoa(I i, char* p) {
|
||||
// Need "mask" to be filled with a copy of the sign bit.
|
||||
// If "i" is a negative value, then the result of "operator >>"
|
||||
// is implementation-defined, though usually it is an arithmetic
|
||||
// right shift that replicates the sign bit.
|
||||
// Use a conditional expression to be portable,
|
||||
// a good optimizing compiler generates an arithmetic right shift
|
||||
// and avoids the conditional branch.
|
||||
U<N> mask = i < 0 ? ~U<N>(0) : 0;
|
||||
// Now get the absolute value of "i" and cast to unsigned type U<N>.
|
||||
// Cannot use std::abs() because the result is undefined
|
||||
// in 2's complement systems for the most-negative value.
|
||||
// Want to avoid conditional branch for performance reasons since
|
||||
// CPU branch prediction will be ineffective when negative values
|
||||
// occur randomly.
|
||||
// Let "u" be "i" cast to unsigned type U<N>.
|
||||
// Subtract "u" from 2*u if "i" is positive or 0 if "i" is negative.
|
||||
// This yields the absolute value with the desired type without
|
||||
// using a conditional branch and without invoking undefined or
|
||||
// implementation defined behavior:
|
||||
U<N> u = ((2 * U<N>(i)) & ~mask) - U<N>(i);
|
||||
// Unconditionally store a minus sign when producing digits
|
||||
// in a forward direction and increment the pointer only if
|
||||
// the value is in fact negative.
|
||||
// This avoids a conditional branch and is safe because we will
|
||||
// always produce at least one digit and it will overwrite the
|
||||
// minus sign when the value is not negative.
|
||||
if (D == Fwd) {
|
||||
*p = '-';
|
||||
p += (mask & 1);
|
||||
}
|
||||
p = convert<D>::template itoa<>(p, u);
|
||||
if (D == Rev && mask)
|
||||
*--p = '-';
|
||||
return p;
|
||||
}
|
||||
};
|
||||
} // namespace dec_
|
||||
|
||||
// Programming interface: itoa_fwd, itoa_rev
|
||||
template <typename I>
|
||||
char* itoa_fwd(I i, char* p) {
|
||||
return dec_::convert<dec_::Fwd>::itoa(i, p);
|
||||
}
|
||||
|
||||
inline char* xtoa(long long sval, char* str, int radix, int signedp) {
|
||||
unsigned long long uval;
|
||||
unsigned int uradix = radix;
|
||||
char* sp = str;
|
||||
char* sp2;
|
||||
char* sp3;
|
||||
|
||||
/* If signed, store sign at start of buffer for negative base-10 values */
|
||||
if (signedp && (10 == uradix) && (0 > sval)) {
|
||||
*sp++ = '-';
|
||||
uval = -sval;
|
||||
}
|
||||
else {
|
||||
uval = sval;
|
||||
}
|
||||
sp2 = sp;
|
||||
|
||||
do {
|
||||
unsigned int rem = uval % uradix;
|
||||
uval /= uradix;
|
||||
if (10 > rem) {
|
||||
*sp++ = '0' + (char)rem;
|
||||
}
|
||||
else {
|
||||
*sp++ = 'A' + (char)rem - 10;
|
||||
}
|
||||
} while (0 < uval);
|
||||
|
||||
/* Mark end of string */
|
||||
sp3 = sp;
|
||||
*sp-- = 0;
|
||||
|
||||
/* Reverse string contents (excluding sign) in place */
|
||||
while (sp2 < sp) {
|
||||
char tmp = *sp2;
|
||||
*sp2++ = *sp;
|
||||
*sp-- = tmp;
|
||||
}
|
||||
|
||||
return sp3;
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
char* itoa_rev(I i, char* p) {
|
||||
return dec_::convert<dec_::Rev>::itoa(i, p);
|
||||
}
|
||||
|
||||
#endif // DEC_ITOA_IMPL_H
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace iguana {
|
||||
|
||||
struct sint32_t {
|
||||
using value_type = int32_t;
|
||||
int32_t val;
|
||||
bool operator==(const sint32_t& other) const { return val == other.val; }
|
||||
};
|
||||
|
||||
inline bool operator==(sint32_t value1, int32_t value2) {
|
||||
return value1.val == value2;
|
||||
}
|
||||
|
||||
// for key in std::map
|
||||
inline bool operator<(const sint32_t& lhs, const sint32_t& rhs) {
|
||||
return lhs.val < rhs.val;
|
||||
}
|
||||
|
||||
struct sint64_t {
|
||||
using value_type = int64_t;
|
||||
int64_t val;
|
||||
bool operator==(const sint64_t& other) const { return val == other.val; }
|
||||
};
|
||||
|
||||
inline bool operator==(sint64_t value1, int64_t value2) {
|
||||
return value1.val == value2;
|
||||
}
|
||||
|
||||
inline bool operator<(const sint64_t& lhs, const sint64_t& rhs) {
|
||||
return lhs.val < rhs.val;
|
||||
}
|
||||
|
||||
struct fixed32_t {
|
||||
using value_type = uint32_t;
|
||||
uint32_t val;
|
||||
bool operator==(const fixed32_t& other) const { return val == other.val; }
|
||||
};
|
||||
|
||||
inline bool operator==(fixed32_t value1, uint32_t value2) {
|
||||
return value1.val == value2;
|
||||
}
|
||||
|
||||
inline bool operator<(const fixed32_t& lhs, const fixed32_t& rhs) {
|
||||
return lhs.val < rhs.val;
|
||||
}
|
||||
|
||||
struct fixed64_t {
|
||||
using value_type = uint64_t;
|
||||
uint64_t val;
|
||||
bool operator==(const fixed64_t& other) const { return val == other.val; }
|
||||
};
|
||||
|
||||
inline bool operator==(fixed64_t value1, uint64_t value2) {
|
||||
return value1.val == value2;
|
||||
}
|
||||
|
||||
inline bool operator<(const fixed64_t& lhs, const fixed64_t& rhs) {
|
||||
return lhs.val < rhs.val;
|
||||
}
|
||||
|
||||
struct sfixed32_t {
|
||||
using value_type = int32_t;
|
||||
int32_t val;
|
||||
bool operator==(const sfixed32_t& other) const { return val == other.val; }
|
||||
};
|
||||
|
||||
inline bool operator==(sfixed32_t value1, int32_t value2) {
|
||||
return value1.val == value2;
|
||||
}
|
||||
|
||||
inline bool operator<(const sfixed32_t& lhs, const sfixed32_t& rhs) {
|
||||
return lhs.val < rhs.val;
|
||||
}
|
||||
|
||||
struct sfixed64_t {
|
||||
using value_type = int64_t;
|
||||
int64_t val;
|
||||
bool operator==(const sfixed64_t& other) const { return val == other.val; }
|
||||
};
|
||||
|
||||
inline bool operator==(sfixed64_t value1, int64_t value2) {
|
||||
return value1.val == value2;
|
||||
}
|
||||
|
||||
inline bool operator<(const sfixed64_t& lhs, const sfixed64_t& rhs) {
|
||||
return lhs.val < rhs.val;
|
||||
}
|
||||
|
||||
template <typename Type, typename T = std::decay_t<Type>>
|
||||
constexpr bool is_pb_type_v =
|
||||
std::is_same_v<sint32_t, T> || std::is_same_v<sint64_t, T> ||
|
||||
std::is_same_v<fixed32_t, T> || std::is_same_v<fixed64_t, T> ||
|
||||
std::is_same_v<sfixed32_t, T> || std::is_same_v<sfixed64_t, T>;
|
||||
|
||||
} // namespace iguana
|
||||
|
||||
// for key in std::unordered_map
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<iguana::sint32_t> {
|
||||
size_t operator()(const iguana::sint32_t& x) const noexcept {
|
||||
return std::hash<int32_t>()(x.val);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<iguana::sint64_t> {
|
||||
size_t operator()(const iguana::sint64_t& x) const noexcept {
|
||||
return std::hash<int64_t>()(x.val);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<iguana::fixed32_t> {
|
||||
size_t operator()(const iguana::fixed32_t& x) const noexcept {
|
||||
return std::hash<uint32_t>()(x.val);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<iguana::fixed64_t> {
|
||||
size_t operator()(const iguana::fixed64_t& x) const noexcept {
|
||||
return std::hash<uint64_t>()(x.val);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<iguana::sfixed32_t> {
|
||||
size_t operator()(const iguana::sfixed32_t& x) const noexcept {
|
||||
return std::hash<int32_t>()(x.val);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<iguana::sfixed64_t> {
|
||||
size_t operator()(const iguana::sfixed64_t& x) const noexcept {
|
||||
return std::hash<int64_t>()(x.val);
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace iguana::detail {
|
||||
|
||||
#if __cpp_lib_string_resize_and_overwrite >= 202110L
|
||||
template <typename ch>
|
||||
inline void resize(std::basic_string<ch> &str, std::size_t sz) {
|
||||
str.resize_and_overwrite(sz, [sz](ch *, std::size_t) {
|
||||
return sz;
|
||||
});
|
||||
}
|
||||
#elif (defined(_MSC_VER) && _MSC_VER <= 1920)
|
||||
// old msvc don't support visit private, discard it.
|
||||
|
||||
#else
|
||||
|
||||
template <typename Function, Function func_ptr>
|
||||
class string_thief {
|
||||
public:
|
||||
friend void string_set_length_hacker(std::string &self, std::size_t sz) {
|
||||
#if defined(_MSVC_STL_VERSION)
|
||||
(self.*func_ptr)._Myval2._Mysize = sz;
|
||||
#else
|
||||
#if defined(_LIBCPP_VERSION)
|
||||
(self.*func_ptr)(sz);
|
||||
#else
|
||||
#if (_GLIBCXX_USE_CXX11_ABI == 0) && defined(__GLIBCXX__)
|
||||
(self.*func_ptr)()->_M_set_length_and_sharable(sz);
|
||||
#else
|
||||
#if defined(__GLIBCXX__)
|
||||
(self.*func_ptr)(sz);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(__GLIBCXX__) // libstdc++
|
||||
#if (_GLIBCXX_USE_CXX11_ABI == 0)
|
||||
template class string_thief<decltype(&std::string::_M_rep),
|
||||
&std::string::_M_rep>;
|
||||
#else
|
||||
template class string_thief<decltype(&std::string::_M_set_length),
|
||||
&std::string::_M_set_length>;
|
||||
#endif
|
||||
#elif defined(_LIBCPP_VERSION)
|
||||
template class string_thief<decltype(&std::string::__set_size),
|
||||
&std::string::__set_size>;
|
||||
#elif defined(_MSVC_STL_VERSION)
|
||||
template class string_thief<decltype(&std::string::_Mypair),
|
||||
&std::string::_Mypair>;
|
||||
#endif
|
||||
|
||||
void string_set_length_hacker(std::string &, std::size_t);
|
||||
|
||||
template <typename ch>
|
||||
inline void resize(std::basic_string<ch> &raw_str, std::size_t sz) {
|
||||
std::string &str = *reinterpret_cast<std::string *>(&raw_str);
|
||||
#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) || \
|
||||
defined(_MSVC_STL_VERSION)
|
||||
if (sz > str.capacity()) {
|
||||
str.reserve(sz);
|
||||
}
|
||||
string_set_length_hacker(str, sz);
|
||||
str[sz] = '\0';
|
||||
#else
|
||||
raw_str.resize(sz);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if (defined(_MSC_VER) && _MSC_VER <= 1920)
|
||||
#else
|
||||
void vector_set_length_hacker(std::vector<char> &self, std::size_t sz);
|
||||
|
||||
template <typename Function, Function func_ptr>
|
||||
class vector_thief {
|
||||
public:
|
||||
friend void vector_set_length_hacker(std::vector<char> &self,
|
||||
std::size_t sz) {
|
||||
#if defined(_MSVC_STL_VERSION)
|
||||
(self.*func_ptr)._Myval2._Mylast = self.data() + sz;
|
||||
#else
|
||||
#if defined(_LIBCPP_VERSION)
|
||||
#if _LIBCPP_VERSION < 14000
|
||||
((*(std::__vector_base<char, std::allocator<char> > *)(&self)).*func_ptr) =
|
||||
self.data() + sz;
|
||||
#else
|
||||
(self.*func_ptr) = self.data() + sz;
|
||||
#endif
|
||||
#else
|
||||
#if defined(__GLIBCXX__)
|
||||
((*(std::_Vector_base<char, std::allocator<char> > *)(&self)).*func_ptr)
|
||||
._M_finish = self.data() + sz;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(__GLIBCXX__) // libstdc++
|
||||
template class vector_thief<decltype(&std::vector<char>::_M_impl),
|
||||
&std::vector<char>::_M_impl>;
|
||||
#elif defined(_LIBCPP_VERSION)
|
||||
template class vector_thief<decltype(&std::vector<char>::__end_),
|
||||
&std::vector<char>::__end_>;
|
||||
#elif defined(_MSVC_STL_VERSION)
|
||||
template class vector_thief<decltype(&std::vector<char>::_Mypair),
|
||||
&std::vector<char>::_Mypair>;
|
||||
#endif
|
||||
|
||||
template <typename ch>
|
||||
inline void resize(std::vector<ch> &raw_vec, std::size_t sz) {
|
||||
#if defined(__GLIBCXX__) || \
|
||||
(defined(_LIBCPP_VERSION) && defined(_LIBCPP_HAS_NO_ASAN)) || \
|
||||
defined(_MSVC_STL_VERSION)
|
||||
std::vector<char> &vec = *reinterpret_cast<std::vector<char> *>(&raw_vec);
|
||||
vec.reserve(sz);
|
||||
vector_set_length_hacker(vec, sz);
|
||||
#else
|
||||
raw_vec.resize(sz);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}; // namespace iguana::detail
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#if __has_include(<memory_resource>)
|
||||
#include <memory_resource>
|
||||
#endif
|
||||
|
||||
namespace iguana {
|
||||
#ifdef IGUANA_ENABLE_PMR
|
||||
#if __has_include(<memory_resource>)
|
||||
inline char iguana_buf[2048];
|
||||
inline std::pmr::monotonic_buffer_resource iguana_resource(iguana_buf, 2048);
|
||||
using string_stream = std::pmr::string;
|
||||
#endif
|
||||
#else
|
||||
using string_stream = std::string;
|
||||
#endif
|
||||
} // namespace iguana
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
//
|
||||
// Created by QY on 2017-01-05.
|
||||
//
|
||||
|
||||
#ifndef SERIALIZE_TRAITS_HPP
|
||||
#define SERIALIZE_TRAITS_HPP
|
||||
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#if __cplusplus > 201703L
|
||||
#if __has_include(<span>)
|
||||
#include <span>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "iguana/define.h"
|
||||
|
||||
namespace iguana {
|
||||
|
||||
template <class T>
|
||||
struct is_signed_intergral_like
|
||||
: std::integral_constant<bool, (std::is_integral<T>::value) &&
|
||||
std::is_signed<T>::value> {};
|
||||
|
||||
template <class T>
|
||||
struct is_unsigned_intergral_like
|
||||
: std::integral_constant<bool, (std::is_integral<T>::value) &&
|
||||
std::is_unsigned<T>::value> {};
|
||||
|
||||
template <template <typename...> class U, typename T>
|
||||
struct is_template_instant_of : std::false_type {};
|
||||
|
||||
template <template <typename...> class U, typename... args>
|
||||
struct is_template_instant_of<U, U<args...>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_stdstring : is_template_instant_of<std::basic_string, T> {};
|
||||
|
||||
template <typename T>
|
||||
struct is_tuple : is_template_instant_of<std::tuple, T> {};
|
||||
|
||||
#if __cplusplus > 201703L
|
||||
#if __has_include(<span>)
|
||||
template <typename>
|
||||
struct is_span : std::false_type {};
|
||||
|
||||
template <typename T, size_t Extent>
|
||||
struct is_span<std::span<T, Extent>> : std::true_type {};
|
||||
#endif
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
struct is_sequence_container
|
||||
: std::integral_constant<
|
||||
bool, is_template_instant_of<std::deque, T>::value ||
|
||||
is_template_instant_of<std::list, T>::value ||
|
||||
is_template_instant_of<std::vector, T>::value ||
|
||||
is_template_instant_of<std::queue, T>::value> {};
|
||||
|
||||
template <class T>
|
||||
struct is_associat_container
|
||||
: std::integral_constant<
|
||||
bool, is_template_instant_of<std::map, T>::value ||
|
||||
is_template_instant_of<std::unordered_map, T>::value> {};
|
||||
|
||||
template <class T>
|
||||
struct is_emplace_back_able
|
||||
: std::integral_constant<
|
||||
bool, is_template_instant_of<std::deque, T>::value ||
|
||||
is_template_instant_of<std::list, T>::value ||
|
||||
is_template_instant_of<std::vector, T>::value> {};
|
||||
|
||||
template <typename T, typename Tuple>
|
||||
struct has_type;
|
||||
|
||||
template <typename T, typename... Us>
|
||||
struct has_type<T, std::tuple<Us...>>
|
||||
: std::disjunction<std::is_same<T, Us>...> {};
|
||||
|
||||
template <class T>
|
||||
struct member_tratis {};
|
||||
|
||||
template <class T, class Owner>
|
||||
struct member_tratis<T Owner::*> {
|
||||
using owner_type = Owner;
|
||||
using value_type = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_int64_v =
|
||||
std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>;
|
||||
|
||||
template <typename T>
|
||||
struct is_variant : std::false_type {};
|
||||
|
||||
template <typename... T>
|
||||
struct is_variant<std::variant<T...>> : std::true_type {};
|
||||
|
||||
template <class T>
|
||||
struct member_traits {
|
||||
using value_type = T;
|
||||
};
|
||||
|
||||
template <class T, class Owner>
|
||||
struct member_traits<T Owner::*> {
|
||||
using owner_type = Owner;
|
||||
using value_type = T;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using member_value_type_t = typename member_traits<T>::value_type;
|
||||
|
||||
template <typename T, std::size_t I, typename = void>
|
||||
struct variant_type_at {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T, std::size_t I>
|
||||
struct variant_type_at<T, I, std::enable_if_t<is_variant<T>::value>> {
|
||||
using type = std::variant_alternative_t<I, T>;
|
||||
};
|
||||
template <typename T, std::size_t I>
|
||||
using variant_type_at_t =
|
||||
typename variant_type_at<typename member_traits<T>::value_type, I>::type;
|
||||
|
||||
/// concepts
|
||||
template <typename Type>
|
||||
constexpr bool is_char_v =
|
||||
std::is_same_v<Type, signed char> || std::is_same_v<Type, char> ||
|
||||
std::is_same_v<Type, unsigned char> || std::is_same_v<Type, wchar_t> ||
|
||||
std::is_same_v<Type, char16_t> || std::is_same_v<Type, char32_t>
|
||||
#ifdef __cpp_lib_char8_t
|
||||
|| std::is_same_v<Type, char8_t>
|
||||
#endif
|
||||
;
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
template <typename Type>
|
||||
concept is_container_v = requires(Type container) {
|
||||
typename std::remove_cvref_t<Type>::value_type;
|
||||
container.size();
|
||||
container.begin();
|
||||
container.end();
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
concept is_resizable_char_container_v = requires(Type container) {
|
||||
typename std::remove_cvref_t<Type>::value_type;
|
||||
container.size();
|
||||
container.begin();
|
||||
container.end();
|
||||
requires is_char_v<typename std::remove_cvref_t<Type>::value_type>;
|
||||
container.resize(std::size_t{});
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept char_writer = requires(T t) {
|
||||
t.write((const char *)nullptr, std::size_t{});
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept char_reader = requires(T t) {
|
||||
t.read((char *)nullptr, std::size_t{});
|
||||
};
|
||||
#else
|
||||
template <typename T, typename = void>
|
||||
struct container_impl : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct container_impl<T,
|
||||
std::void_t<typename std::remove_cvref_t<T>::value_type,
|
||||
decltype(std::declval<T>().size()),
|
||||
decltype(std::declval<T>().begin()),
|
||||
decltype(std::declval<T>().end())>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_container_v = container_impl<T>::value;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct resizable_char_container_impl : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct resizable_char_container_impl<
|
||||
T, std::void_t<typename std::remove_cvref_t<T>::value_type,
|
||||
decltype(std::declval<T>().size()),
|
||||
decltype(std::declval<T>().begin()),
|
||||
decltype(std::declval<T>().end()),
|
||||
std::enable_if_t<
|
||||
is_char_v<typename std::remove_cvref_t<T>::value_type>>,
|
||||
decltype(std::declval<T>().resize(std::size_t{}))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_resizable_char_container_v =
|
||||
resizable_char_container_impl<T>::value;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct char_writer_impl : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct char_writer_impl<T, std::void_t<decltype(std::declval<T>().write(
|
||||
(const char *)nullptr, std::size_t{}))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool char_writer = char_writer_impl<T>::value;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct char_reader_impl : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct char_reader_impl<T, std::void_t<decltype(std::declval<T>().read(
|
||||
(char *)nullptr, std::size_t{}))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool char_reader = char_reader_impl<T>::value;
|
||||
#endif
|
||||
|
||||
} // namespace iguana
|
||||
#endif // SERIALIZE_TRAITS_HPP
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "iguana/define.h"
|
||||
|
||||
namespace iguana {
|
||||
// https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/reader.h
|
||||
template <typename Ch = char, typename It>
|
||||
inline unsigned parse_unicode_hex4(It &&it) {
|
||||
unsigned codepoint = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Ch c = *it;
|
||||
codepoint <<= 4;
|
||||
codepoint += static_cast<unsigned>(c);
|
||||
if (c >= '0' && c <= '9')
|
||||
codepoint -= '0';
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
codepoint -= 'A' - 10;
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
codepoint -= 'a' - 10;
|
||||
else {
|
||||
throw std::runtime_error("Invalid Unicode Escape Hex");
|
||||
}
|
||||
++it;
|
||||
}
|
||||
return codepoint;
|
||||
}
|
||||
|
||||
// https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/encodings.h
|
||||
template <typename Ch = char, typename OutputStream>
|
||||
inline void encode_utf8(OutputStream &os, unsigned codepoint) {
|
||||
if (codepoint > 0x10FFFF)
|
||||
IGUANA_UNLIKELY {
|
||||
throw std::runtime_error("Invalid unicode codepoint:" +
|
||||
std::to_string(codepoint));
|
||||
}
|
||||
if (codepoint <= 0x7F)
|
||||
os.push_back(static_cast<Ch>(codepoint & 0xFF));
|
||||
else if (codepoint <= 0x7FF) {
|
||||
os.push_back(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
|
||||
os.push_back(static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
|
||||
}
|
||||
else if (codepoint <= 0xFFFF) {
|
||||
os.push_back(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
|
||||
os.push_back(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
os.push_back(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
else {
|
||||
os.push_back(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
|
||||
os.push_back(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
|
||||
os.push_back(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
os.push_back(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/encodings.h
|
||||
static inline unsigned char GetRange(unsigned char c) {
|
||||
static const unsigned char type[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0x10, 0x10, 0x10, 0x10,
|
||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
|
||||
0x40, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 10, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3,
|
||||
11, 6, 6, 6, 5, 8, 8, 8, 8, 8, 8, 8,
|
||||
8, 8, 8, 8,
|
||||
};
|
||||
return type[c];
|
||||
}
|
||||
|
||||
// https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/encodings.h
|
||||
template <typename Ch = char, typename It>
|
||||
inline bool decode_utf8(It &&it, unsigned &codepoint) {
|
||||
auto c = *(it++);
|
||||
bool result = true;
|
||||
auto copy = [&]() IGUANA__INLINE_LAMBDA {
|
||||
c = *(it++);
|
||||
codepoint = (codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu);
|
||||
};
|
||||
auto trans = [&](unsigned mask) IGUANA__INLINE_LAMBDA {
|
||||
result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0);
|
||||
};
|
||||
auto tail = [&]() IGUANA__INLINE_LAMBDA {
|
||||
copy();
|
||||
trans(0x70);
|
||||
};
|
||||
if (!(c & 0x80)) {
|
||||
codepoint = static_cast<unsigned char>(c);
|
||||
return true;
|
||||
}
|
||||
unsigned char type = GetRange(static_cast<unsigned char>(c));
|
||||
if (type >= 32) {
|
||||
codepoint = 0;
|
||||
}
|
||||
else {
|
||||
codepoint = (0xFFu >> type) & static_cast<unsigned char>(c);
|
||||
}
|
||||
switch (type) {
|
||||
case 2:
|
||||
tail();
|
||||
return result;
|
||||
case 3:
|
||||
tail();
|
||||
tail();
|
||||
return result;
|
||||
case 4:
|
||||
copy();
|
||||
trans(0x50);
|
||||
tail();
|
||||
return result;
|
||||
case 5:
|
||||
copy();
|
||||
trans(0x10);
|
||||
tail();
|
||||
tail();
|
||||
return result;
|
||||
case 6:
|
||||
tail();
|
||||
tail();
|
||||
tail();
|
||||
return result;
|
||||
case 10:
|
||||
copy();
|
||||
trans(0x20);
|
||||
tail();
|
||||
return result;
|
||||
case 11:
|
||||
copy();
|
||||
trans(0x60);
|
||||
tail();
|
||||
tail();
|
||||
return result;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace iguana
|
||||
Reference in New Issue
Block a user