Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
wjr-z committed Aug 16, 2024
1 parent f2cbda0 commit 5251573
Show file tree
Hide file tree
Showing 12 changed files with 271 additions and 21 deletions.
4 changes: 2 additions & 2 deletions include/wjr/assert.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ WJR_NORETURN extern void __assert_failed(const char *expr, const char *file,

/// @private
template <typename... Args>
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>(args));
std::cerr << '\n';
Expand Down
3 changes: 2 additions & 1 deletion include/wjr/biginteger/biginteger.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint32_t>(reverse_find_not_n(ptr, 0, n));
}

Expand Down
13 changes: 13 additions & 0 deletions include/wjr/container/generic/detail.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,19 @@ WJR_INTRINSIC_INLINE void try_uninitialized_resize(Container &cont, Size sz) {
}
}

template <typename Container, typename Size,
WJR_REQUIRES(has_container_append_v<Container, Size> ||
has_container_resize_v<Container, Size>)>
WJR_INTRINSIC_INLINE void try_uninitialized_append(Container &cont, Size sz) {
if constexpr (has_container_append_v<Container, Size, dctor_t>) {
append(cont, sz, dctor);
} else if constexpr (has_container_append_v<Container, Size>) {
append(cont, sz);
} else {
try_uninitialized_resize(cont, cont.size() + sz);
}
}

/// @private
template <typename T, typename = void>
struct __container_traits_base_iterator_helper {
Expand Down
7 changes: 1 addition & 6 deletions include/wjr/container/generic/vector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<size_type>(old_capacity + (old_capacity / 2) + __small_growth,
new_size);
return std::max<size_type>(old_capacity * 2, new_size);
}

private:
Expand Down
4 changes: 2 additions & 2 deletions include/wjr/format/charconv.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ template <uint64_t Base>
inline constexpr __from_chars_unroll_16_fn<Base> __from_chars_unroll_16{};

template <typename UnsignedValue>
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)) {
Expand Down Expand Up @@ -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<Iter> *) \
__unsigned_to_chars_backward_unchecked<BASE>( \
(uint8_t *)__end, WJR_PP_QUEUE_EXPAND(CALL), conv); \
Expand Down
228 changes: 228 additions & 0 deletions include/wjr/json/formatter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
#ifndef WJR_FORMATTER_HPP__
#define WJR_FORMATTER_HPP__

#include <wjr/container/generic/bitset.hpp>
#include <wjr/format/charconv.hpp>
#include <wjr/format/dragonbox.hpp>
#include <wjr/json/detail.hpp>
#include <wjr/vector.hpp>

namespace wjr::json {

namespace formatter_detail {

template <typename Container>
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 <typename Container>
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 <typename Inserter, typename formatter>
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<double>);
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 <typename Inserter>
class minify_formatter : public base_formatter<Inserter, minify_formatter<Inserter>> {
using Mybase = base_formatter<Inserter, minify_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 <typename Inserter>
minify_formatter(Inserter iter) -> minify_formatter<Inserter>;

} // namespace wjr::json

#endif // WJR_FORMATTER_HPP__
6 changes: 3 additions & 3 deletions include/wjr/math/add.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ WJR_INTRINSIC_CONSTEXPR20 T addc(T a, T b, type_identity_t<U> 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
Expand Down
2 changes: 1 addition & 1 deletion include/wjr/math/clz.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ WJR_CONST WJR_INTRINSIC_INLINE int builtin_clz(T x) noexcept {
template <typename T, WJR_REQUIRES(is_nonbool_unsigned_integral_v<T>)>
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);
}

Expand Down
2 changes: 1 addition & 1 deletion include/wjr/math/ctz.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ WJR_CONST WJR_INTRINSIC_INLINE int builtin_ctz(T x) noexcept {
template <typename T, WJR_REQUIRES(is_nonbool_unsigned_integral_v<T>)>
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);
}

Expand Down
7 changes: 3 additions & 4 deletions include/wjr/math/detail.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ WJR_CONST constexpr T __align_up_offset(T n, type_identity_t<T> alignment) noexc
template <typename T, WJR_REQUIRES(is_nonbool_unsigned_integral_v<T>)>
WJR_CONST constexpr std::make_signed_t<T> __fasts_from_unsigned(T x) noexcept {
const std::make_signed_t<T> ret = x;
WJR_ASSERT_ASSUME_L2(ret >= 0, "overflow");
WJR_ASSERT_ASSUME_L2(ret >= 0);
return ret;
}

Expand Down Expand Up @@ -94,15 +94,14 @@ WJR_CONST constexpr T __fasts_negate_with(T condition, T x) noexcept {
template <typename T, WJR_REQUIRES(is_nonbool_signed_integral_v<T>)>
WJR_CONST constexpr T __fasts_increment(T x) noexcept {
WJR_ASSERT_L2(x != std::numeric_limits<T>::min() &&
x != std::numeric_limits<T>::max(),
"overflow");
x != std::numeric_limits<T>::max());

return x < 0 ? x - 1 : x + 1;
}

template <typename T, WJR_REQUIRES(is_nonbool_signed_integral_v<T>)>
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;
}
Expand Down
8 changes: 8 additions & 0 deletions include/wjr/preprocessor/compiler/attribute.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion include/wjr/string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,13 @@ template <typename CharT, typename Traits, typename Alloc>
WJR_INTRINSIC_INLINE void
__uninitialized_resize(std::basic_string<CharT, Traits, Alloc> &str,
typename std::basic_string<CharT, Traits, Alloc>::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';
Expand Down

0 comments on commit 5251573

Please sign in to comment.