diff --git a/include/wjr/assert.hpp b/include/wjr/assert.hpp index 02357492..470548a6 100644 --- a/include/wjr/assert.hpp +++ b/include/wjr/assert.hpp @@ -55,8 +55,8 @@ WJR_NORETURN extern void __assert_failed(const char *expr, const char *file, /// @private template -WJR_NOINLINE void __assert_handler(const char *expr, const char *file, const char *func, - int line, Args &&...args) noexcept { +void __assert_handler(const char *expr, const char *file, const char *func, int line, + Args &&...args) noexcept { std::cerr << "Additional information: "; (void)(std::cerr << ... << std::forward(args)); std::cerr << '\n'; diff --git a/include/wjr/biginteger/biginteger.hpp b/include/wjr/biginteger/biginteger.hpp index 75963ae1..476016bc 100644 --- a/include/wjr/biginteger/biginteger.hpp +++ b/include/wjr/biginteger/biginteger.hpp @@ -17,7 +17,8 @@ namespace biginteger_detail { * @brief Remove leading zeros. * */ -inline uint32_t normalize(const uint64_t *ptr, uint32_t n) noexcept { +WJR_PURE inline WJR_CONSTEXPR20 uint32_t normalize(const uint64_t *ptr, + uint32_t n) noexcept { return static_cast(reverse_find_not_n(ptr, 0, n)); } diff --git a/include/wjr/container/generic/detail.hpp b/include/wjr/container/generic/detail.hpp index 71dcd191..21d11f18 100644 --- a/include/wjr/container/generic/detail.hpp +++ b/include/wjr/container/generic/detail.hpp @@ -93,6 +93,19 @@ WJR_INTRINSIC_INLINE void try_uninitialized_resize(Container &cont, Size sz) { } } +template || + has_container_resize_v)> +WJR_INTRINSIC_INLINE void try_uninitialized_append(Container &cont, Size sz) { + if constexpr (has_container_append_v) { + append(cont, sz, dctor); + } else if constexpr (has_container_append_v) { + append(cont, sz); + } else { + try_uninitialized_resize(cont, cont.size() + sz); + } +} + /// @private template struct __container_traits_base_iterator_helper { diff --git a/include/wjr/container/generic/vector.hpp b/include/wjr/container/generic/vector.hpp index 62fe2515..39cb143c 100644 --- a/include/wjr/container/generic/vector.hpp +++ b/include/wjr/container/generic/vector.hpp @@ -802,12 +802,7 @@ class basic_vector { WJR_CONST WJR_CONSTEXPR20 static size_type get_growth_capacity(size_type old_capacity, size_type new_size) noexcept { - constexpr size_type __small_growth = sizeof(value_type) <= 16 ? 2 - : sizeof(value_type) <= 64 ? 1 - : 0; - - return std::max(old_capacity + (old_capacity / 2) + __small_growth, - new_size); + return std::max(old_capacity * 2, new_size); } private: diff --git a/include/wjr/format/charconv.hpp b/include/wjr/format/charconv.hpp index c82e4611..73c3e158 100644 --- a/include/wjr/format/charconv.hpp +++ b/include/wjr/format/charconv.hpp @@ -518,7 +518,7 @@ template inline constexpr __from_chars_unroll_16_fn __from_chars_unroll_16{}; template -constexpr int fallback_count_digits10(UnsignedValue n) noexcept { +WJR_INTRINSIC_CONSTEXPR int fallback_count_digits10(UnsignedValue n) noexcept { int count = 0; if (WJR_UNLIKELY(n >= 1000)) { @@ -1298,7 +1298,7 @@ Iter __fallback_to_chars_unchecked_impl(Iter ptr, Value val, IBase ibase, } else { \ append(cont, n + sign, dctor); \ } \ - const auto __end = wjr::to_address(cont.data() + cont.size()); \ + auto *const __end = cont.data() + cont.size(); \ auto __ptr = (charconv_detail::fast_buffer_t *) \ __unsigned_to_chars_backward_unchecked( \ (uint8_t *)__end, WJR_PP_QUEUE_EXPAND(CALL), conv); \ diff --git a/include/wjr/json/formatter.hpp b/include/wjr/json/formatter.hpp new file mode 100644 index 00000000..828e8b46 --- /dev/null +++ b/include/wjr/json/formatter.hpp @@ -0,0 +1,228 @@ +#ifndef WJR_FORMATTER_HPP__ +#define WJR_FORMATTER_HPP__ + +#include +#include +#include +#include +#include + +namespace wjr::json { + +namespace formatter_detail { + +template +WJR_INTRINSIC_INLINE void append_string(Container &cont, const char *str, size_t length) { + try_uninitialized_append(cont, length); + std::memcpy(cont.data() + cont.size() - length, str, length); +} + +struct escape_sequence { + uint8_t length; + const char string[7]; // technically, we only ever need 6 characters, we pad to 8 +}; + +template +WJR_INTRINSIC_INLINE void format_string(Container &cont, std::string_view str) { + const auto length = str.size(); + const auto old_size = cont.size(); + try_uninitialized_append(cont, length + 2); + auto *ptr = cont.data() + old_size; + *ptr++ = '\"'; + + auto first = str.data(); + + size_t i = 0; + // Fast path for the case where we have no control character, no ", and no backslash. + // This should include most keys. + // + // We would like to use 'bool' but some compilers take offense to bitwise operation + // with bool types. + constexpr static char needs_escaping[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 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, 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}; + + for (; i + 8 <= length; i += 8) { + // Poor's man vectorization. This could get much faster if we used SIMD. + // + // It is not the case that replacing '|' with '||' would be neutral + // performance-wise. + if (needs_escaping[uint8_t(first[i])] | needs_escaping[uint8_t(first[i + 1])] | + needs_escaping[uint8_t(first[i + 2])] | + needs_escaping[uint8_t(first[i + 3])] | + needs_escaping[uint8_t(first[i + 4])] | + needs_escaping[uint8_t(first[i + 5])] | + needs_escaping[uint8_t(first[i + 6])] | + needs_escaping[uint8_t(first[i + 7])]) { + goto SLOW_PATH; + } + } + + for (; i < length; i++) { + if (needs_escaping[uint8_t(first[i])]) { + goto SLOW_PATH; + } + } + + // The following is also possible and omits a 256-byte table, but it is slower: + // for (; (i < unescaped.length()) && (uint8_t(unescaped[i]) > 0x1F) + // && (unescaped[i] != '\"') && (unescaped[i] != '\\'); i++) {} + + // At least for long strings, the following should be fast. We could + // do better by integrating the checks and the insertion. + std::memcpy(ptr, first, length); + ptr += length; + *ptr++ = '\"'; + return; + +SLOW_PATH : { + std::memcpy(ptr, first, i); + ptr += i; + try_uninitialized_resize(cont, ptr - cont.data()); + + // We caught a control character if we enter this loop (slow). + // Note that we are do not restart from the beginning, but rather we continue + // from the point where we encountered something that requires escaping. + for (; i < length; i++) { + switch (first[i]) { + case '\"': { + append_string(cont, "\\\"", 2); + break; + } + case '\\': { + append_string(cont, "\\\\", 2); + break; + } + default: + if (uint8_t(first[i]) <= 0x1F) { + // If packed, this uses 8 * 32 bytes. + // Note that we expect most compilers to embed this code in the data + // section. + constexpr static escape_sequence escaped[32] = { + {6, "\\u0000"}, {6, "\\u0001"}, {6, "\\u0002"}, {6, "\\u0003"}, + {6, "\\u0004"}, {6, "\\u0005"}, {6, "\\u0006"}, {6, "\\u0007"}, + {2, "\\b"}, {2, "\\t"}, {2, "\\n"}, {6, "\\u000b"}, + {2, "\\f"}, {2, "\\r"}, {6, "\\u000e"}, {6, "\\u000f"}, + {6, "\\u0010"}, {6, "\\u0011"}, {6, "\\u0012"}, {6, "\\u0013"}, + {6, "\\u0014"}, {6, "\\u0015"}, {6, "\\u0016"}, {6, "\\u0017"}, + {6, "\\u0018"}, {6, "\\u0019"}, {6, "\\u001a"}, {6, "\\u001b"}, + {6, "\\u001c"}, {6, "\\u001d"}, {6, "\\u001e"}, {6, "\\u001f"}}; + auto u = escaped[uint8_t(first[i])]; + if (u.length == 2) { + append_string(cont, u.string, 2); + } else { + append_string(cont, u.string, 6); + } + } else { + cont.push_back(first[i]); + } + } // switch + } // for + + cont.push_back('\"'); + return; +} +} + +} // namespace formatter_detail + +template +class base_formatter { +protected: + WJR_INTRINSIC_INLINE void __append_str(const char *str, size_t length) { + formatter_detail::append_string(get_inserter_container(m_iter), str, length); + } + + WJR_INTRINSIC_INLINE void __append_char(char ch) { *m_iter++ = ch; } + +public: + base_formatter(Inserter iter) noexcept : m_iter(iter) {} + + base_formatter() = delete; + base_formatter(const base_formatter &) = default; + base_formatter(base_formatter &&) = default; + base_formatter &operator=(const base_formatter &) = default; + base_formatter &operator=(base_formatter &&) = default; + ~base_formatter() = default; + + /// @brief Prints a null + WJR_INTRINSIC_INLINE void format_null() { __append_str("null", 4); } + /// @brief Prints a true + WJR_INTRINSIC_INLINE void format_true() { __append_str("true", 4); } + /// @brief Prints a false + WJR_INTRINSIC_INLINE void format_false() { __append_str("false", 5); } + /// @brief Start an object + WJR_INTRINSIC_INLINE void format_start_object() { __append_char('{'); } + /// @brief End an object + WJR_INTRINSIC_INLINE void format_end_object() { __append_char('}'); } + /// @brief Start an array + WJR_INTRINSIC_INLINE void format_start_array() { __append_char('['); } + /// @brief End an array + WJR_INTRINSIC_INLINE void format_end_array() { __append_char(']'); } + /// @brief Add a comma + WJR_INTRINSIC_INLINE void format_comma() { __append_char(','); } + /// @brief Prints a number + WJR_INTRINSIC_INLINE void format_number_unsigned(uint64_t x) { + to_chars_unchecked(m_iter, x); + } + /// @brief Prints a number + WJR_INTRINSIC_INLINE void format_number_signed(int64_t x) { + to_chars_unchecked(m_iter, x); + } + /// @brief Prints a number + WJR_INTRINSIC_INLINE void format_number_float(double x) { + auto &cont = get_inserter_container(m_iter); + const auto old_size = cont.size(); + try_uninitialized_append(cont, dragonbox::max_output_string_length_of); + auto *const ptr = cont.data() + old_size; + try_uninitialized_resize(cont, (dragonbox::to_chars(x, ptr) - cont.data())); + } + /// @brief Prints a key (string + colon) + WJR_INTRINSIC_INLINE void format_key(std::string_view str) { + format_string(str); + __append_char(':'); + } + + /// @brief Prints a string. The string is escaped as needed. + WJR_INTRINSIC_INLINE void format_string(std::string_view str) { + formatter_detail::format_string(get_inserter_container(m_iter), str); + } + + WJR_INTRINSIC_INLINE void format_newline() { this->print_newline(); } + + WJR_INTRINSIC_INLINE void format_indents(size_t depth) { this->print_indents(depth); } + + WJR_INTRINSIC_INLINE void format_space() { this->print_space(); } + +protected: + Inserter m_iter; +}; + +template +class minify_formatter : public base_formatter> { + using Mybase = base_formatter; + +public: + using Mybase::Mybase; + + WJR_INTRINSIC_INLINE void print_newline() {} + + WJR_INTRINSIC_INLINE void print_indents(size_t) {} + + WJR_INTRINSIC_INLINE void print_space() {} +}; + +template +minify_formatter(Inserter iter) -> minify_formatter; + +} // namespace wjr::json + +#endif // WJR_FORMATTER_HPP__ \ No newline at end of file diff --git a/include/wjr/math/add.hpp b/include/wjr/math/add.hpp index 3b7f046e..d4f2daa4 100644 --- a/include/wjr/math/add.hpp +++ b/include/wjr/math/add.hpp @@ -73,9 +73,9 @@ WJR_INTRINSIC_CONSTEXPR20 T addc(T a, T b, type_identity_t c_in, U &c_out) no return fallback_addc(a, b, c_in, c_out); #else constexpr auto is_constant_or_zero = [](auto x) -> int { - return WJR_BUILTIN_CONSTANT_P(x == 0) && x == 0 ? 2 - : WJR_BUILTIN_CONSTANT_P(x) ? 1 - : 0; + return WJR_BUILTIN_CONSTANT_P_TRUE(x == 0) ? 2 + : WJR_BUILTIN_CONSTANT_P(x) ? 1 + : 0; }; // The compiler should be able to optimize the judgment condition of if when enabling diff --git a/include/wjr/math/clz.hpp b/include/wjr/math/clz.hpp index 19c34d5a..7d52b3d8 100644 --- a/include/wjr/math/clz.hpp +++ b/include/wjr/math/clz.hpp @@ -129,7 +129,7 @@ WJR_CONST WJR_INTRINSIC_INLINE int builtin_clz(T x) noexcept { template )> WJR_CONST WJR_INTRINSIC_CONSTEXPR20 int clz(T x) noexcept { #if WJR_HAS_BUILTIN(CLZ) - if (is_constant_evaluated() || WJR_BUILTIN_CONSTANT_P(x)) { + if (is_constant_evaluated()) { return fallback_clz(x); } diff --git a/include/wjr/math/ctz.hpp b/include/wjr/math/ctz.hpp index b8feb6f0..b91b96d1 100644 --- a/include/wjr/math/ctz.hpp +++ b/include/wjr/math/ctz.hpp @@ -93,7 +93,7 @@ WJR_CONST WJR_INTRINSIC_INLINE int builtin_ctz(T x) noexcept { template )> WJR_CONST WJR_INTRINSIC_CONSTEXPR20 int ctz(T x) noexcept { #if WJR_HAS_BUILTIN(CTZ) - if (is_constant_evaluated() || WJR_BUILTIN_CONSTANT_P(x)) { + if (is_constant_evaluated()) { return fallback_ctz(x); } diff --git a/include/wjr/math/detail.hpp b/include/wjr/math/detail.hpp index 6512733b..340a96ed 100644 --- a/include/wjr/math/detail.hpp +++ b/include/wjr/math/detail.hpp @@ -64,7 +64,7 @@ WJR_CONST constexpr T __align_up_offset(T n, type_identity_t alignment) noexc template )> WJR_CONST constexpr std::make_signed_t __fasts_from_unsigned(T x) noexcept { const std::make_signed_t ret = x; - WJR_ASSERT_ASSUME_L2(ret >= 0, "overflow"); + WJR_ASSERT_ASSUME_L2(ret >= 0); return ret; } @@ -94,15 +94,14 @@ WJR_CONST constexpr T __fasts_negate_with(T condition, T x) noexcept { template )> WJR_CONST constexpr T __fasts_increment(T x) noexcept { WJR_ASSERT_L2(x != std::numeric_limits::min() && - x != std::numeric_limits::max(), - "overflow"); + x != std::numeric_limits::max()); return x < 0 ? x - 1 : x + 1; } template )> WJR_CONST constexpr T __fasts_decrement(T x) noexcept { - WJR_ASSERT_L2(x != 0 && x + 1 != T(0), "overflow"); + WJR_ASSERT_L2(x != 0 && x + 1 != T(0)); return x < 0 ? x + 1 : x - 1; } diff --git a/include/wjr/preprocessor/compiler/attribute.hpp b/include/wjr/preprocessor/compiler/attribute.hpp index 449b16a1..4a227b8b 100644 --- a/include/wjr/preprocessor/compiler/attribute.hpp +++ b/include/wjr/preprocessor/compiler/attribute.hpp @@ -71,6 +71,14 @@ #define WJR_SAFEBUFFERS #endif +#if WJR_HAS_ATTRIBUTE(flatten) +#define WJR_FLATTEN __attribute__((flatten)) +#elif WJR_HAS_MSVC(17, 00) +#define WJR_FLATTEN [[msvc::flatten]] +#else +#define WJR_FLATTEN +#endif + #if WJR_HAS_FEATURE(FORCEINLINE_LAMBDA) #define WJR_FORCEINLINE_LAMBDA WJR_FORCEINLINE #else diff --git a/include/wjr/string.hpp b/include/wjr/string.hpp index c0f6c059..8388e7c5 100644 --- a/include/wjr/string.hpp +++ b/include/wjr/string.hpp @@ -86,7 +86,13 @@ template WJR_INTRINSIC_INLINE void __uninitialized_resize(std::basic_string &str, typename std::basic_string::size_type sz) { - str.reserve(sz); +#if !defined(WJR_CXX_20) + if (sz > str.capacity()) { +#endif + str.reserve(sz); +#if !defined(WJR_CXX_20) + } +#endif string_set_length_hacker(str, sz); WJR_ASSERT_L2(str.size() == sz); str[sz] = '\0';