diff options
author | chertus <azuikov@ydb.tech> | 2022-11-23 16:16:44 +0300 |
---|---|---|
committer | chertus <azuikov@ydb.tech> | 2022-11-23 16:16:44 +0300 |
commit | 742ef5b7c36c67b958b5eac7ddfd019ddae39ec1 (patch) | |
tree | ddcb8c275fbfb70b6d944e72883ec8973cf45772 | |
parent | 837e447ad5eca521acde76ff230a27a8d406acaa (diff) | |
download | ydb-742ef5b7c36c67b958b5eac7ddfd019ddae39ec1.tar.gz |
remove UDF dependency
19 files changed, 2368 insertions, 15 deletions
diff --git a/ydb/library/arrow_clickhouse/CMakeLists.txt b/ydb/library/arrow_clickhouse/CMakeLists.txt index afe895bcb27..b625dac455e 100644 --- a/ydb/library/arrow_clickhouse/CMakeLists.txt +++ b/ydb/library/arrow_clickhouse/CMakeLists.txt @@ -13,7 +13,7 @@ add_subdirectory(ut) add_library(ydb-library-arrow_clickhouse) target_include_directories(ydb-library-arrow_clickhouse PUBLIC - ${CMAKE_SOURCE_DIR}/ydb/library/yql/udfs/common/clickhouse/client/base + ${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse/base ) target_include_directories(ydb-library-arrow_clickhouse PRIVATE ${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse @@ -29,6 +29,5 @@ target_link_libraries(ydb-library-arrow_clickhouse PUBLIC target_sources(ydb-library-arrow_clickhouse PRIVATE ${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse/AggregateFunctions/IAggregateFunction.cpp ${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse/Aggregator.cpp - ${CMAKE_SOURCE_DIR}/ydb/library/yql/udfs/common/clickhouse/client/base/common/mremap.cpp - ${CMAKE_SOURCE_DIR}/ydb/library/yql/udfs/common/clickhouse/client/base/common/getPageSize.cpp + ${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse/base/common/mremap.cpp ) diff --git a/ydb/library/arrow_clickhouse/Columns/CMakeLists.txt b/ydb/library/arrow_clickhouse/Columns/CMakeLists.txt index 0f4c153a983..7fb88cc9cce 100644 --- a/ydb/library/arrow_clickhouse/Columns/CMakeLists.txt +++ b/ydb/library/arrow_clickhouse/Columns/CMakeLists.txt @@ -9,7 +9,7 @@ add_library(library-arrow_clickhouse-Columns) target_include_directories(library-arrow_clickhouse-Columns PRIVATE - ${CMAKE_SOURCE_DIR}/ydb/library/yql/udfs/common/clickhouse/client/base + ${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse/base ${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse ) target_link_libraries(library-arrow_clickhouse-Columns PUBLIC diff --git a/ydb/library/arrow_clickhouse/Common/Allocator.cpp b/ydb/library/arrow_clickhouse/Common/Allocator.cpp index 96a2ae6ad0a..39478230c36 100644 --- a/ydb/library/arrow_clickhouse/Common/Allocator.cpp +++ b/ydb/library/arrow_clickhouse/Common/Allocator.cpp @@ -3,10 +3,16 @@ // See: https://github.com/ClickHouse/ClickHouse/ #include <Common/Allocator.h> +#include <unistd.h> namespace CH { +int64_t getPageSize() +{ + return sysconf(_SC_PAGESIZE); +} + /** Keep definition of this constant in cpp file; otherwise its value * is inlined into allocator code making it impossible to override it * in third-party code. diff --git a/ydb/library/arrow_clickhouse/Common/Allocator.h b/ydb/library/arrow_clickhouse/Common/Allocator.h index 46fcc75719a..f9902cee159 100644 --- a/ydb/library/arrow_clickhouse/Common/Allocator.h +++ b/ydb/library/arrow_clickhouse/Common/Allocator.h @@ -24,7 +24,7 @@ #include <common/mremap.h> -#include <common/getPageSize.h> +//#include <common/getPageSize.h> #include <Common/Allocator_fwd.h> @@ -36,6 +36,9 @@ namespace CH { +/// Get memory page size +int64_t getPageSize(); + /** * Many modern allocators (for example, tcmalloc) do not do a mremap for * realloc, even in case of large enough chunks of memory. Although this allows @@ -175,7 +178,7 @@ private: void * allocNoTrack(size_t size, size_t alignment) { void * buf; - size_t mmap_min_alignment = ::getPageSize(); + size_t mmap_min_alignment = getPageSize(); if (size >= MMAP_THRESHOLD) { diff --git a/ydb/library/arrow_clickhouse/Common/Arena.h b/ydb/library/arrow_clickhouse/Common/Arena.h index 7f86e70e5ae..1633a29ad7a 100644 --- a/ydb/library/arrow_clickhouse/Common/Arena.h +++ b/ydb/library/arrow_clickhouse/Common/Arena.h @@ -119,7 +119,7 @@ public: explicit Arena(size_t initial_size_ = 4096, size_t growth_factor_ = 2, size_t linear_growth_threshold_ = 128 * 1024 * 1024) : growth_factor(growth_factor_), linear_growth_threshold(linear_growth_threshold_), head(new MemoryChunk(initial_size_, nullptr)), size_in_bytes(head->size()), - page_size(static_cast<size_t>(::getPageSize())) + page_size(static_cast<size_t>(getPageSize())) { } diff --git a/ydb/library/arrow_clickhouse/Common/CMakeLists.txt b/ydb/library/arrow_clickhouse/Common/CMakeLists.txt index a455b526c0b..d792835d45e 100644 --- a/ydb/library/arrow_clickhouse/Common/CMakeLists.txt +++ b/ydb/library/arrow_clickhouse/Common/CMakeLists.txt @@ -9,7 +9,7 @@ add_library(library-arrow_clickhouse-Common) target_include_directories(library-arrow_clickhouse-Common PRIVATE - ${CMAKE_SOURCE_DIR}/ydb/library/yql/udfs/common/clickhouse/client/base + ${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse/base ${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse ) target_link_libraries(library-arrow_clickhouse-Common PUBLIC diff --git a/ydb/library/arrow_clickhouse/Common/PODArray.h b/ydb/library/arrow_clickhouse/Common/PODArray.h index 45924b02225..cc398c67cd6 100644 --- a/ydb/library/arrow_clickhouse/Common/PODArray.h +++ b/ydb/library/arrow_clickhouse/Common/PODArray.h @@ -12,8 +12,7 @@ #include <memory> //#include <boost/noncopyable.hpp> - -#include <common/strong_typedef.h> +//#include <common/strong_typedef.h> #include <Common/Allocator.h> #include <Common/memcpySmall.h> diff --git a/ydb/library/arrow_clickhouse/DataStreams/CMakeLists.txt b/ydb/library/arrow_clickhouse/DataStreams/CMakeLists.txt index a150b18b741..e4db05eb18e 100644 --- a/ydb/library/arrow_clickhouse/DataStreams/CMakeLists.txt +++ b/ydb/library/arrow_clickhouse/DataStreams/CMakeLists.txt @@ -9,7 +9,7 @@ add_library(library-arrow_clickhouse-DataStreams) target_include_directories(library-arrow_clickhouse-DataStreams PRIVATE - ${CMAKE_SOURCE_DIR}/ydb/library/yql/udfs/common/clickhouse/client/base + ${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse/base ${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse ) target_link_libraries(library-arrow_clickhouse-DataStreams PUBLIC diff --git a/ydb/library/arrow_clickhouse/arrow_clickhouse_types.h b/ydb/library/arrow_clickhouse/arrow_clickhouse_types.h index f88eac64dd8..9142899fb14 100644 --- a/ydb/library/arrow_clickhouse/arrow_clickhouse_types.h +++ b/ydb/library/arrow_clickhouse/arrow_clickhouse_types.h @@ -20,10 +20,6 @@ namespace CH { -using NDB::StringRef; -using NDB::StringRefHash; -using NDB::StringRefs; - /// What to do if the limit is exceeded. enum class OverflowMode { diff --git a/ydb/library/arrow_clickhouse/base/common/StringRef.h b/ydb/library/arrow_clickhouse/base/common/StringRef.h new file mode 100644 index 00000000000..d6fccd2522e --- /dev/null +++ b/ydb/library/arrow_clickhouse/base/common/StringRef.h @@ -0,0 +1,204 @@ +#pragma once + +#include <cassert> +#include <stdexcept> // for std::logic_error +#include <string> +#include <vector> +#include <functional> +#include <iosfwd> + +#include <common/types.h> +#include <common/unaligned.h> + +#if defined(__SSE2__) + #include <emmintrin.h> +#endif + +#if defined(__SSE4_2__) + #include <smmintrin.h> + #include <nmmintrin.h> +#endif + +namespace CH +{ + +/** + * The std::string_view-like container to avoid creating strings to find substrings in the hash table. + */ +struct StringRef +{ + const char * data = nullptr; + size_t size = 0; + + /// Non-constexpr due to reinterpret_cast. + template <typename CharT, typename = std::enable_if_t<sizeof(CharT) == 1>> + StringRef(const CharT * data_, size_t size_) : data(reinterpret_cast<const char *>(data_)), size(size_) + { + /// Sanity check for overflowed values. + assert(size < 0x8000000000000000ULL); + } + + constexpr StringRef(const char * data_, size_t size_) : data(data_), size(size_) {} + + StringRef(const std::string & s) : data(s.data()), size(s.size()) {} + constexpr explicit StringRef(std::string_view s) : data(s.data()), size(s.size()) {} + constexpr StringRef(const char * data_) : StringRef(std::string_view{data_}) {} + constexpr StringRef() = default; + + std::string toString() const { return std::string(data, size); } + + explicit operator std::string() const { return toString(); } + constexpr explicit operator std::string_view() const { return {data, size}; } +}; + +/// Here constexpr doesn't implicate inline, see https://www.viva64.com/en/w/v1043/ +/// nullptr can't be used because the StringRef values are used in SipHash's pointer arithmetic +/// and the UBSan thinks that something like nullptr + 8 is UB. +constexpr const inline char empty_string_ref_addr{}; +constexpr const inline StringRef EMPTY_STRING_REF{&empty_string_ref_addr, 0}; + +using StringRefs = std::vector<StringRef>; + + +#if defined(__SSE2__) + +/** Compare strings for equality. + * The approach is controversial and does not win in all cases. + * For more information, see hash_map_string_2.cpp + */ + +inline bool compareSSE2(const char * p1, const char * p2) +{ + return 0xFFFF == _mm_movemask_epi8(_mm_cmpeq_epi8( + _mm_loadu_si128(reinterpret_cast<const __m128i *>(p1)), + _mm_loadu_si128(reinterpret_cast<const __m128i *>(p2)))); +} + +inline bool compareSSE2x4(const char * p1, const char * p2) +{ + return 0xFFFF == _mm_movemask_epi8( + _mm_and_si128( + _mm_and_si128( + _mm_cmpeq_epi8( + _mm_loadu_si128(reinterpret_cast<const __m128i *>(p1)), + _mm_loadu_si128(reinterpret_cast<const __m128i *>(p2))), + _mm_cmpeq_epi8( + _mm_loadu_si128(reinterpret_cast<const __m128i *>(p1) + 1), + _mm_loadu_si128(reinterpret_cast<const __m128i *>(p2) + 1))), + _mm_and_si128( + _mm_cmpeq_epi8( + _mm_loadu_si128(reinterpret_cast<const __m128i *>(p1) + 2), + _mm_loadu_si128(reinterpret_cast<const __m128i *>(p2) + 2)), + _mm_cmpeq_epi8( + _mm_loadu_si128(reinterpret_cast<const __m128i *>(p1) + 3), + _mm_loadu_si128(reinterpret_cast<const __m128i *>(p2) + 3))))); +} + +inline bool memequalSSE2Wide(const char * p1, const char * p2, size_t size) +{ + while (size >= 64) + { + if (compareSSE2x4(p1, p2)) + { + p1 += 64; + p2 += 64; + size -= 64; + } + else + return false; + } + + switch ((size % 64) / 16) + { + case 3: if (!compareSSE2(p1 + 32, p2 + 32)) return false; [[fallthrough]]; + case 2: if (!compareSSE2(p1 + 16, p2 + 16)) return false; [[fallthrough]]; + case 1: if (!compareSSE2(p1 , p2 )) return false; [[fallthrough]]; + case 0: break; + } + + p1 += (size % 64) / 16 * 16; + p2 += (size % 64) / 16 * 16; + + switch (size % 16) + { + case 15: if (p1[14] != p2[14]) return false; [[fallthrough]]; + case 14: if (p1[13] != p2[13]) return false; [[fallthrough]]; + case 13: if (p1[12] != p2[12]) return false; [[fallthrough]]; + case 12: if (unalignedLoad<uint32_t>(p1 + 8) == unalignedLoad<uint32_t>(p2 + 8)) goto l8; else return false; + case 11: if (p1[10] != p2[10]) return false; [[fallthrough]]; + case 10: if (p1[9] != p2[9]) return false; [[fallthrough]]; + case 9: if (p1[8] != p2[8]) return false; + l8: [[fallthrough]]; + case 8: return unalignedLoad<uint64_t>(p1) == unalignedLoad<uint64_t>(p2); + case 7: if (p1[6] != p2[6]) return false; [[fallthrough]]; + case 6: if (p1[5] != p2[5]) return false; [[fallthrough]]; + case 5: if (p1[4] != p2[4]) return false; [[fallthrough]]; + case 4: return unalignedLoad<uint32_t>(p1) == unalignedLoad<uint32_t>(p2); + case 3: if (p1[2] != p2[2]) return false; [[fallthrough]]; + case 2: return unalignedLoad<uint16_t>(p1) == unalignedLoad<uint16_t>(p2); + case 1: if (p1[0] != p2[0]) return false; [[fallthrough]]; + case 0: break; + } + + return true; +} + +#endif + + +inline bool operator== (StringRef lhs, StringRef rhs) +{ + if (lhs.size != rhs.size) + return false; + + if (lhs.size == 0) + return true; + +#if defined(__SSE2__) + return memequalSSE2Wide(lhs.data, rhs.data, lhs.size); +#else + return 0 == memcmp(lhs.data, rhs.data, lhs.size); +#endif +} + +inline bool operator!= (StringRef lhs, StringRef rhs) +{ + return !(lhs == rhs); +} + +inline bool operator< (StringRef lhs, StringRef rhs) +{ + int cmp = memcmp(lhs.data, rhs.data, std::min(lhs.size, rhs.size)); + return cmp < 0 || (cmp == 0 && lhs.size < rhs.size); +} + +inline bool operator> (StringRef lhs, StringRef rhs) +{ + int cmp = memcmp(lhs.data, rhs.data, std::min(lhs.size, rhs.size)); + return cmp > 0 || (cmp == 0 && lhs.size > rhs.size); +} + +struct StringRefHash { + size_t operator() (StringRef x) const + { + return std::hash<std::string_view>()(std::string_view(x)); + } +}; + +} + +namespace std +{ + template <> + struct hash<CH::StringRef> : public CH::StringRefHash {}; +} + + +namespace ZeroTraits +{ + inline bool check(const CH::StringRef & x) { return 0 == x.size; } + inline void set(CH::StringRef & x) { x.size = 0; } +} + + +std::ostream & operator<<(std::ostream & os, const CH::StringRef & str); diff --git a/ydb/library/arrow_clickhouse/base/common/defines.h b/ydb/library/arrow_clickhouse/base/common/defines.h new file mode 100644 index 00000000000..ada8245f494 --- /dev/null +++ b/ydb/library/arrow_clickhouse/base/common/defines.h @@ -0,0 +1,124 @@ +#pragma once + +/// __has_feature supported only by clang. +/// +/// But libcxx/libcxxabi overrides it to 0, +/// thus the checks for __has_feature will be wrong. +/// +/// NOTE: +/// - __has_feature cannot be simply undefined, +/// since this will be broken if some C++ header will be included after +/// including <common/defines.h> +/// - it should not have fallback to 0, +/// since this may create false-positive detection (common problem) +#if defined(__clang__) && defined(__has_feature) +# define ch_has_feature __has_feature +#endif + +#if defined(_MSC_VER) +# if !defined(likely) +# define likely(x) (x) +# endif +# if !defined(unlikely) +# define unlikely(x) (x) +# endif +#else +# if !defined(likely) +# define likely(x) (__builtin_expect(!!(x), 1)) +# endif +# if !defined(unlikely) +# define unlikely(x) (__builtin_expect(!!(x), 0)) +# endif +#endif + +#if defined(_MSC_VER) +# define ALWAYS_INLINE __forceinline +# define NO_INLINE static __declspec(noinline) +# define MAY_ALIAS +#else +# define ALWAYS_INLINE __attribute__((__always_inline__)) +# define NO_INLINE __attribute__((__noinline__)) +# define MAY_ALIAS __attribute__((__may_alias__)) +#endif + +#if !defined(__x86_64__) && !defined(__aarch64__) && !defined(__PPC__) +# error "The only supported platforms are x86_64 and AArch64, PowerPC (work in progress)" +#endif + +/// Check for presence of address sanitizer +#if !defined(ADDRESS_SANITIZER) +# if defined(ch_has_feature) +# if ch_has_feature(address_sanitizer) +# define ADDRESS_SANITIZER 1 +# endif +# elif defined(__SANITIZE_ADDRESS__) +# define ADDRESS_SANITIZER 1 +# endif +#endif + +#if !defined(THREAD_SANITIZER) +# if defined(ch_has_feature) +# if ch_has_feature(thread_sanitizer) +# define THREAD_SANITIZER 1 +# endif +# elif defined(__SANITIZE_THREAD__) +# define THREAD_SANITIZER 1 +# endif +#endif + +#if !defined(MEMORY_SANITIZER) +# if defined(ch_has_feature) +# if ch_has_feature(memory_sanitizer) +# define MEMORY_SANITIZER 1 +# endif +# elif defined(__MEMORY_SANITIZER__) +# define MEMORY_SANITIZER 1 +# endif +#endif + +#if !defined(UNDEFINED_BEHAVIOR_SANITIZER) +# if defined(__has_feature) +# if __has_feature(undefined_behavior_sanitizer) +# define UNDEFINED_BEHAVIOR_SANITIZER 1 +# endif +# elif defined(__UNDEFINED_BEHAVIOR_SANITIZER__) +# define UNDEFINED_BEHAVIOR_SANITIZER 1 +# endif +#endif + +#if defined(ADDRESS_SANITIZER) +# define BOOST_USE_ASAN 1 +# define BOOST_USE_UCONTEXT 1 +#endif + +#if defined(THREAD_SANITIZER) +# define BOOST_USE_TSAN 1 +# define BOOST_USE_UCONTEXT 1 +#endif + +#if defined(ARCADIA_BUILD) && defined(BOOST_USE_UCONTEXT) +# undef BOOST_USE_UCONTEXT +#endif + +/// TODO: Strange enough, there is no way to detect UB sanitizer. + +/// Explicitly allow undefined behaviour for certain functions. Use it as a function attribute. +/// It is useful in case when compiler cannot see (and exploit) it, but UBSan can. +/// Example: multiplication of signed integers with possibility of overflow when both sides are from user input. +#if defined(__clang__) +# define NO_SANITIZE_UNDEFINED __attribute__((__no_sanitize__("undefined"))) +# define NO_SANITIZE_ADDRESS __attribute__((__no_sanitize__("address"))) +# define NO_SANITIZE_THREAD __attribute__((__no_sanitize__("thread"))) +# define ALWAYS_INLINE_NO_SANITIZE_UNDEFINED __attribute__((__always_inline__, __no_sanitize__("undefined"))) +#else /// It does not work in GCC. GCC 7 cannot recognize this attribute and GCC 8 simply ignores it. +# define NO_SANITIZE_UNDEFINED +# define NO_SANITIZE_ADDRESS +# define NO_SANITIZE_THREAD +# define ALWAYS_INLINE_NO_SANITIZE_UNDEFINED ALWAYS_INLINE +#endif + +/// A template function for suppressing warnings about unused variables or function results. +template <typename... Args> +constexpr void UNUSED(Args &&... args [[maybe_unused]]) +{ +} diff --git a/ydb/library/arrow_clickhouse/base/common/extended_types.h b/ydb/library/arrow_clickhouse/base/common/extended_types.h new file mode 100644 index 00000000000..e7b2cd7f352 --- /dev/null +++ b/ydb/library/arrow_clickhouse/base/common/extended_types.h @@ -0,0 +1,118 @@ +#pragma once + +#include <type_traits> + +#include <common/types.h> +#include <common/wide_integer.h> + +namespace CH +{ + +using Int128 = wide::integer<128, signed>; +using UInt128 = wide::integer<128, unsigned>; +using Int256 = wide::integer<256, signed>; +using UInt256 = wide::integer<256, unsigned>; + +static_assert(sizeof(Int256) == 32); +static_assert(sizeof(UInt256) == 32); + +/// The standard library type traits, such as std::is_arithmetic, with one exception +/// (std::common_type), are "set in stone". Attempting to specialize them causes undefined behavior. +/// So instead of using the std type_traits, we use our own version which allows extension. +template <typename T> +struct is_signed +{ + static constexpr bool value = std::is_signed_v<T>; +}; + +template <> struct is_signed<Int128> { static constexpr bool value = true; }; +template <> struct is_signed<Int256> { static constexpr bool value = true; }; + +template <typename T> +inline constexpr bool is_signed_v = is_signed<T>::value; + +template <typename T> +struct is_unsigned +{ + static constexpr bool value = std::is_unsigned_v<T>; +}; + +template <> struct is_unsigned<UInt128> { static constexpr bool value = true; }; +template <> struct is_unsigned<UInt256> { static constexpr bool value = true; }; + +template <typename T> +inline constexpr bool is_unsigned_v = is_unsigned<T>::value; + + +/// TODO: is_integral includes char, char8_t and wchar_t. +template <typename T> +struct is_integer +{ + static constexpr bool value = std::is_integral_v<T>; +}; + +template <> struct is_integer<Int128> { static constexpr bool value = true; }; +template <> struct is_integer<UInt128> { static constexpr bool value = true; }; +template <> struct is_integer<Int256> { static constexpr bool value = true; }; +template <> struct is_integer<UInt256> { static constexpr bool value = true; }; + +template <typename T> +inline constexpr bool is_integer_v = is_integer<T>::value; + + +template <typename T> +struct is_arithmetic +{ + static constexpr bool value = std::is_arithmetic_v<T>; +}; + +template <> struct is_arithmetic<Int128> { static constexpr bool value = true; }; +template <> struct is_arithmetic<UInt128> { static constexpr bool value = true; }; +template <> struct is_arithmetic<Int256> { static constexpr bool value = true; }; +template <> struct is_arithmetic<UInt256> { static constexpr bool value = true; }; + + +template <typename T> +inline constexpr bool is_arithmetic_v = is_arithmetic<T>::value; + +template <typename T> +struct make_unsigned +{ + typedef std::make_unsigned_t<T> type; +}; + +template <> struct make_unsigned<Int128> { using type = UInt128; }; +template <> struct make_unsigned<UInt128> { using type = UInt128; }; +template <> struct make_unsigned<Int256> { using type = UInt256; }; +template <> struct make_unsigned<UInt256> { using type = UInt256; }; + +template <typename T> using make_unsigned_t = typename make_unsigned<T>::type; + +template <typename T> +struct make_signed +{ + typedef std::make_signed_t<T> type; +}; + +template <> struct make_signed<Int128> { using type = Int128; }; +template <> struct make_signed<UInt128> { using type = Int128; }; +template <> struct make_signed<Int256> { using type = Int256; }; +template <> struct make_signed<UInt256> { using type = Int256; }; + +template <typename T> using make_signed_t = typename make_signed<T>::type; + +template <typename T> +struct is_big_int +{ + static constexpr bool value = false; +}; + +template <> struct is_big_int<Int128> { static constexpr bool value = true; }; +template <> struct is_big_int<UInt128> { static constexpr bool value = true; }; +template <> struct is_big_int<Int256> { static constexpr bool value = true; }; +template <> struct is_big_int<UInt256> { static constexpr bool value = true; }; + +template <typename T> +inline constexpr bool is_big_int_v = is_big_int<T>::value; + +} diff --git a/ydb/library/arrow_clickhouse/base/common/mremap.cpp b/ydb/library/arrow_clickhouse/base/common/mremap.cpp new file mode 100644 index 00000000000..d2d8d7fde4f --- /dev/null +++ b/ydb/library/arrow_clickhouse/base/common/mremap.cpp @@ -0,0 +1,40 @@ +#include <common/mremap.h> + +#include <cstddef> +#include <cstdlib> +#include <cstring> +#include <errno.h> + + +void * mremap_fallback( + void * old_address, size_t old_size, size_t new_size, int flags, int mmap_prot, int mmap_flags, int mmap_fd, off_t mmap_offset) +{ + /// No actual shrink + if (new_size < old_size) + return old_address; + + if (!(flags & MREMAP_MAYMOVE)) + { + errno = ENOMEM; + return MAP_FAILED; + } + +#if defined(_MSC_VER) + void * new_address = ::operator new(new_size); +#else + void * new_address = mmap(nullptr, new_size, mmap_prot, mmap_flags, mmap_fd, mmap_offset); + if (MAP_FAILED == new_address) + return MAP_FAILED; +#endif + + memcpy(new_address, old_address, old_size); + +#if defined(_MSC_VER) + delete old_address; +#else + if (munmap(old_address, old_size)) + abort(); +#endif + + return new_address; +} diff --git a/ydb/library/arrow_clickhouse/base/common/mremap.h b/ydb/library/arrow_clickhouse/base/common/mremap.h new file mode 100644 index 00000000000..f488393278a --- /dev/null +++ b/ydb/library/arrow_clickhouse/base/common/mremap.h @@ -0,0 +1,85 @@ +#pragma once + +#include <cstddef> +#include <sys/types.h> +#if !defined(_MSC_VER) +#include <sys/mman.h> +#endif + + +#ifdef MREMAP_MAYMOVE + #define HAS_MREMAP 1 +#else + #define HAS_MREMAP 0 +#endif + + +/// You can forcely disable mremap by defining DISABLE_MREMAP to 1 before including this file. +#ifndef DISABLE_MREMAP + #if HAS_MREMAP + #define DISABLE_MREMAP 0 + #else + #define DISABLE_MREMAP 1 + #endif +#endif + + +/// Implement mremap with mmap/memcpy/munmap. +void * mremap_fallback( + void * old_address, + size_t old_size, + size_t new_size, + int flags, + int mmap_prot, + int mmap_flags, + int mmap_fd, + off_t mmap_offset); + + +#if !HAS_MREMAP + #define MREMAP_MAYMOVE 1 + + inline void * mremap( + void * old_address, + size_t old_size, + size_t new_size, + int flags = 0, + int mmap_prot = 0, + int mmap_flags = 0, + int mmap_fd = -1, + off_t mmap_offset = 0) + { + return mremap_fallback(old_address, old_size, new_size, flags, mmap_prot, mmap_flags, mmap_fd, mmap_offset); + } +#endif + + +inline void * clickhouse_mremap( + void * old_address, + size_t old_size, + size_t new_size, + int flags = 0, + [[maybe_unused]] int mmap_prot = 0, + [[maybe_unused]] int mmap_flags = 0, + [[maybe_unused]] int mmap_fd = -1, + [[maybe_unused]] off_t mmap_offset = 0) +{ +#if DISABLE_MREMAP + return mremap_fallback(old_address, old_size, new_size, flags, mmap_prot, mmap_flags, mmap_fd, mmap_offset); +#else + + return mremap( + old_address, + old_size, + new_size, + flags +#if !defined(MREMAP_FIXED) + , + mmap_prot, + mmap_flags, + mmap_fd, + mmap_offset +#endif + ); +#endif +} diff --git a/ydb/library/arrow_clickhouse/base/common/types.h b/ydb/library/arrow_clickhouse/base/common/types.h new file mode 100644 index 00000000000..ff04c566797 --- /dev/null +++ b/ydb/library/arrow_clickhouse/base/common/types.h @@ -0,0 +1,31 @@ +#pragma once + +#include <cstdint> +#include <string> + +namespace CH +{ + +using Int8 = int8_t; +using Int16 = int16_t; +using Int32 = int32_t; +using Int64 = int64_t; + +#ifndef __cpp_char8_t +using char8_t = unsigned char; +#endif + +/// This is needed for more strict aliasing. https://godbolt.org/z/xpJBSb https://stackoverflow.com/a/57453713 +#if !defined(PVS_STUDIO) /// But PVS-Studio does not treat it correctly. +using UInt8 = char8_t; +#else +using UInt8 = uint8_t; +#endif + +using UInt16 = uint16_t; +using UInt32 = uint32_t; +using UInt64 = uint64_t; + +using String = std::string; + +} diff --git a/ydb/library/arrow_clickhouse/base/common/unaligned.h b/ydb/library/arrow_clickhouse/base/common/unaligned.h new file mode 100644 index 00000000000..9119153e2e0 --- /dev/null +++ b/ydb/library/arrow_clickhouse/base/common/unaligned.h @@ -0,0 +1,29 @@ +#pragma once + +#include <string.h> +#include <type_traits> + +namespace CH +{ + +template <typename T> +inline T unalignedLoad(const void * address) +{ + T res {}; + memcpy(&res, address, sizeof(res)); + return res; +} + +/// We've had troubles before with wrong store size due to integral promotions +/// (e.g., unalignedStore(dest, uint16_t + uint16_t) stores an uint32_t). +/// To prevent this, make the caller specify the stored type explicitly. +/// To disable deduction of T, wrap the argument type with std::enable_if. +template <typename T> +inline void unalignedStore(void * address, + const typename std::enable_if<true, T>::type & src) +{ + static_assert(std::is_trivially_copyable_v<T>); + memcpy(address, &src, sizeof(src)); +} + +} diff --git a/ydb/library/arrow_clickhouse/base/common/wide_integer.h b/ydb/library/arrow_clickhouse/base/common/wide_integer.h new file mode 100644 index 00000000000..27f8152923c --- /dev/null +++ b/ydb/library/arrow_clickhouse/base/common/wide_integer.h @@ -0,0 +1,260 @@ +#pragma once + +/////////////////////////////////////////////////////////////// +// Distributed under the Boost Software License, Version 1.0. +// (See at http://www.boost.org/LICENSE_1_0.txt) +/////////////////////////////////////////////////////////////// + +/* Divide and multiply + * + * + * Copyright (c) 2008 + * Evan Teran + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appears in all copies and that both the + * copyright notice and this permission notice appear in supporting + * documentation, and that the same name not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. We make no representations about the + * suitability this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include <cstdint> +#include <limits> +#include <type_traits> +#include <initializer_list> + +namespace CH::wide +{ +template <size_t Bits, typename Signed> +class integer; +} + +namespace std +{ + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +struct common_type<CH::wide::integer<Bits, Signed>, CH::wide::integer<Bits2, Signed2>>; + +template <size_t Bits, typename Signed, typename Arithmetic> +struct common_type<CH::wide::integer<Bits, Signed>, Arithmetic>; + +template <typename Arithmetic, size_t Bits, typename Signed> +struct common_type<Arithmetic, CH::wide::integer<Bits, Signed>>; + +} + +namespace CH::wide +{ + +template <size_t Bits, typename Signed> +class integer +{ +public: + using base_type = uint64_t; + using signed_base_type = int64_t; + + // ctors + constexpr integer() noexcept = default; + + template <typename T> + constexpr integer(T rhs) noexcept; + + template <typename T> + constexpr integer(std::initializer_list<T> il) noexcept; + + // assignment + template <size_t Bits2, typename Signed2> + constexpr integer<Bits, Signed> & operator=(const integer<Bits2, Signed2> & rhs) noexcept; + + template <typename Arithmetic> + constexpr integer<Bits, Signed> & operator=(Arithmetic rhs) noexcept; + + template <typename Arithmetic> + constexpr integer<Bits, Signed> & operator*=(const Arithmetic & rhs); + + template <typename Arithmetic> + constexpr integer<Bits, Signed> & operator/=(const Arithmetic & rhs); + + template <typename Arithmetic> + constexpr integer<Bits, Signed> & operator+=(const Arithmetic & rhs) noexcept(std::is_same_v<Signed, unsigned>); + + template <typename Arithmetic> + constexpr integer<Bits, Signed> & operator-=(const Arithmetic & rhs) noexcept(std::is_same_v<Signed, unsigned>); + + template <typename Integral> + constexpr integer<Bits, Signed> & operator%=(const Integral & rhs); + + template <typename Integral> + constexpr integer<Bits, Signed> & operator&=(const Integral & rhs) noexcept; + + template <typename Integral> + constexpr integer<Bits, Signed> & operator|=(const Integral & rhs) noexcept; + + template <typename Integral> + constexpr integer<Bits, Signed> & operator^=(const Integral & rhs) noexcept; + + constexpr integer<Bits, Signed> & operator<<=(int n) noexcept; + constexpr integer<Bits, Signed> & operator>>=(int n) noexcept; + + constexpr integer<Bits, Signed> & operator++() noexcept(std::is_same_v<Signed, unsigned>); + constexpr integer<Bits, Signed> operator++(int) noexcept(std::is_same_v<Signed, unsigned>); + constexpr integer<Bits, Signed> & operator--() noexcept(std::is_same_v<Signed, unsigned>); + constexpr integer<Bits, Signed> operator--(int) noexcept(std::is_same_v<Signed, unsigned>); + + // observers + + constexpr explicit operator bool() const noexcept; + + template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>, T>> + constexpr operator T() const noexcept; + + constexpr operator long double() const noexcept; + constexpr operator double() const noexcept; + constexpr operator float() const noexcept; + + struct _impl; + + base_type items[_impl::item_count]; + +private: + template <size_t Bits2, typename Signed2> + friend class integer; + + friend class std::numeric_limits<integer<Bits, signed>>; + friend class std::numeric_limits<integer<Bits, unsigned>>; +}; + +template <typename T> +static constexpr bool ArithmeticConcept() noexcept; + +template <class T1, class T2> +using _only_arithmetic = typename std::enable_if<ArithmeticConcept<T1>() && ArithmeticConcept<T2>()>::type; + +template <typename T> +static constexpr bool IntegralConcept() noexcept; + +template <class T, class T2> +using _only_integer = typename std::enable_if<IntegralConcept<T>() && IntegralConcept<T2>()>::type; + +// Unary operators +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed> operator~(const integer<Bits, Signed> & lhs) noexcept; + +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed> operator-(const integer<Bits, Signed> & lhs) noexcept(std::is_same_v<Signed, unsigned>); + +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed> operator+(const integer<Bits, Signed> & lhs) noexcept(std::is_same_v<Signed, unsigned>); + +// Binary operators +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr +operator*(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs); +template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>> +std::common_type_t<Arithmetic, Arithmetic2> constexpr operator*(const Arithmetic & rhs, const Arithmetic2 & lhs); + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr +operator/(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs); +template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>> +std::common_type_t<Arithmetic, Arithmetic2> constexpr operator/(const Arithmetic & rhs, const Arithmetic2 & lhs); + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr +operator+(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs); +template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>> +std::common_type_t<Arithmetic, Arithmetic2> constexpr operator+(const Arithmetic & rhs, const Arithmetic2 & lhs); + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr +operator-(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs); +template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>> +std::common_type_t<Arithmetic, Arithmetic2> constexpr operator-(const Arithmetic & rhs, const Arithmetic2 & lhs); + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr +operator%(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs); +template <typename Integral, typename Integral2, class = _only_integer<Integral, Integral2>> +std::common_type_t<Integral, Integral2> constexpr operator%(const Integral & rhs, const Integral2 & lhs); + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr +operator&(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs); +template <typename Integral, typename Integral2, class = _only_integer<Integral, Integral2>> +std::common_type_t<Integral, Integral2> constexpr operator&(const Integral & rhs, const Integral2 & lhs); + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr +operator|(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs); +template <typename Integral, typename Integral2, class = _only_integer<Integral, Integral2>> +std::common_type_t<Integral, Integral2> constexpr operator|(const Integral & rhs, const Integral2 & lhs); + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr +operator^(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs); +template <typename Integral, typename Integral2, class = _only_integer<Integral, Integral2>> +std::common_type_t<Integral, Integral2> constexpr operator^(const Integral & rhs, const Integral2 & lhs); + +// TODO: Integral +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed> operator<<(const integer<Bits, Signed> & lhs, int n) noexcept; + +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed> operator>>(const integer<Bits, Signed> & lhs, int n) noexcept; + +template <size_t Bits, typename Signed, typename Int, typename = std::enable_if_t<!std::is_same_v<Int, int>>> +constexpr integer<Bits, Signed> operator<<(const integer<Bits, Signed> & lhs, Int n) noexcept +{ + return lhs << int(n); +} +template <size_t Bits, typename Signed, typename Int, typename = std::enable_if_t<!std::is_same_v<Int, int>>> +constexpr integer<Bits, Signed> operator>>(const integer<Bits, Signed> & lhs, Int n) noexcept +{ + return lhs >> int(n); +} + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +constexpr bool operator<(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs); +template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>> +constexpr bool operator<(const Arithmetic & rhs, const Arithmetic2 & lhs); + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +constexpr bool operator>(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs); +template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>> +constexpr bool operator>(const Arithmetic & rhs, const Arithmetic2 & lhs); + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +constexpr bool operator<=(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs); +template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>> +constexpr bool operator<=(const Arithmetic & rhs, const Arithmetic2 & lhs); + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +constexpr bool operator>=(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs); +template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>> +constexpr bool operator>=(const Arithmetic & rhs, const Arithmetic2 & lhs); + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +constexpr bool operator==(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs); +template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>> +constexpr bool operator==(const Arithmetic & rhs, const Arithmetic2 & lhs); + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +constexpr bool operator!=(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs); +template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>> +constexpr bool operator!=(const Arithmetic & rhs, const Arithmetic2 & lhs); + +} + +namespace std +{ + +template <size_t Bits, typename Signed> +struct hash<CH::wide::integer<Bits, Signed>>; + +} + +#include "wide_integer_impl.h" diff --git a/ydb/library/arrow_clickhouse/base/common/wide_integer_impl.h b/ydb/library/arrow_clickhouse/base/common/wide_integer_impl.h new file mode 100644 index 00000000000..d3ddaf1431e --- /dev/null +++ b/ydb/library/arrow_clickhouse/base/common/wide_integer_impl.h @@ -0,0 +1,1455 @@ +#pragma once + +/// Original is here https://github.com/cerevra/int +/// Distributed under the Boost Software License, Version 1.0. +/// (See at http://www.boost.org/LICENSE_1_0.txt) + +#include <cmath> +#include <cfloat> +#include <cassert> +#include <tuple> +#include <limits> + + +namespace CH::wide +{ + +template <typename T> +struct IsWideInteger +{ + static const constexpr bool value = false; +}; + +template <size_t Bits, typename Signed> +struct IsWideInteger<wide::integer<Bits, Signed>> +{ + static const constexpr bool value = true; +}; + +template <typename T> +static constexpr bool ArithmeticConcept() noexcept +{ + return std::is_arithmetic_v<T> || IsWideInteger<T>::value; +} + +template <typename T> +static constexpr bool IntegralConcept() noexcept +{ + return std::is_integral_v<T> || IsWideInteger<T>::value; +} + +template <typename T> +class IsTupleLike +{ + template <typename U> + static auto check(U * p) -> decltype(std::tuple_size<U>::value, int()); + template <typename> + static void check(...); + +public: + static constexpr const bool value = !std::is_void<decltype(check<T>(nullptr))>::value; +}; + +} + +namespace std +{ + +// numeric limits +template <size_t Bits, typename Signed> +class numeric_limits<CH::wide::integer<Bits, Signed>> +{ +public: + static constexpr bool is_specialized = true; + static constexpr bool is_signed = is_same<Signed, signed>::value; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = true; + static constexpr std::float_denorm_style has_denorm = std::denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr std::float_round_style round_style = std::round_toward_zero; + static constexpr bool is_iec559 = false; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr int digits = Bits - (is_same<Signed, signed>::value ? 1 : 0); + static constexpr int digits10 = digits * 0.30103 /*std::log10(2)*/; + static constexpr int max_digits10 = 0; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + + static constexpr CH::wide::integer<Bits, Signed> min() noexcept + { + if (is_same<Signed, signed>::value) + { + using T = CH::wide::integer<Bits, signed>; + T res{}; + res.items[T::_impl::big(0)] = std::numeric_limits<typename CH::wide::integer<Bits, Signed>::signed_base_type>::min(); + return res; + } + return CH::wide::integer<Bits, Signed>(0); + } + + static constexpr CH::wide::integer<Bits, Signed> max() noexcept + { + using T = CH::wide::integer<Bits, Signed>; + T res{}; + res.items[T::_impl::big(0)] = is_same<Signed, signed>::value + ? std::numeric_limits<typename CH::wide::integer<Bits, Signed>::signed_base_type>::max() + : std::numeric_limits<typename CH::wide::integer<Bits, Signed>::base_type>::max(); + for (unsigned i = 1; i < CH::wide::integer<Bits, Signed>::_impl::item_count; ++i) + { + res.items[T::_impl::big(i)] = std::numeric_limits<typename CH::wide::integer<Bits, Signed>::base_type>::max(); + } + return res; + } + + static constexpr CH::wide::integer<Bits, Signed> lowest() noexcept { return min(); } + static constexpr CH::wide::integer<Bits, Signed> epsilon() noexcept { return 0; } + static constexpr CH::wide::integer<Bits, Signed> round_error() noexcept { return 0; } + static constexpr CH::wide::integer<Bits, Signed> infinity() noexcept { return 0; } + static constexpr CH::wide::integer<Bits, Signed> quiet_NaN() noexcept { return 0; } + static constexpr CH::wide::integer<Bits, Signed> signaling_NaN() noexcept { return 0; } + static constexpr CH::wide::integer<Bits, Signed> denorm_min() noexcept { return 0; } +}; + +// type traits +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +struct common_type<CH::wide::integer<Bits, Signed>, CH::wide::integer<Bits2, Signed2>> +{ + using type = std::conditional_t < Bits == Bits2, + CH::wide::integer< + Bits, + std::conditional_t<(std::is_same_v<Signed, Signed2> && std::is_same_v<Signed2, signed>), signed, unsigned>>, + std::conditional_t<Bits2<Bits, CH::wide::integer<Bits, Signed>, CH::wide::integer<Bits2, Signed2>>>; +}; + +template <size_t Bits, typename Signed, typename Arithmetic> +struct common_type<CH::wide::integer<Bits, Signed>, Arithmetic> +{ + static_assert(CH::wide::ArithmeticConcept<Arithmetic>()); + + using type = std::conditional_t< + std::is_floating_point_v<Arithmetic>, + Arithmetic, + std::conditional_t< + sizeof(Arithmetic) < Bits * sizeof(long), + CH::wide::integer<Bits, Signed>, + std::conditional_t< + Bits * sizeof(long) < sizeof(Arithmetic), + Arithmetic, + std::conditional_t< + Bits * sizeof(long) == sizeof(Arithmetic) && (std::is_same_v<Signed, signed> || std::is_signed_v<Arithmetic>), + Arithmetic, + CH::wide::integer<Bits, Signed>>>>>; +}; + +template <typename Arithmetic, size_t Bits, typename Signed> +struct common_type<Arithmetic, CH::wide::integer<Bits, Signed>> : common_type<CH::wide::integer<Bits, Signed>, Arithmetic> +{ +}; + +} + +namespace CH::wide +{ + +template <size_t Bits, typename Signed> +struct integer<Bits, Signed>::_impl +{ + static constexpr size_t _bits = Bits; + static constexpr const unsigned byte_count = Bits / 8; + static constexpr const unsigned item_count = byte_count / sizeof(base_type); + static constexpr const unsigned base_bits = sizeof(base_type) * 8; + + static_assert(Bits % base_bits == 0); + + /// Simple iteration in both directions + static constexpr unsigned little(unsigned idx) { return idx; } + static constexpr unsigned big(unsigned idx) { return item_count - 1 - idx; } + static constexpr unsigned any(unsigned idx) { return idx; } + + template <class T> + constexpr static bool is_negative(const T & n) noexcept + { + if constexpr (std::is_signed_v<T>) + return n < 0; + else + return false; + } + + template <size_t B, class T> + constexpr static bool is_negative(const integer<B, T> & n) noexcept + { + if constexpr (std::is_same_v<T, signed>) + return static_cast<signed_base_type>(n.items[integer<B, T>::_impl::big(0)]) < 0; + else + return false; + } + + template <typename T> + constexpr static auto make_positive(const T & n) noexcept + { + if constexpr (std::is_signed_v<T>) + return n < 0 ? -n : n; + else + return n; + } + + template <size_t B, class S> + constexpr static integer<B, S> make_positive(const integer<B, S> & n) noexcept + { + return is_negative(n) ? integer<B, S>(operator_unary_minus(n)) : n; + } + + template <typename T> + __attribute__((no_sanitize("undefined"))) constexpr static auto to_Integral(T f) noexcept + { + if constexpr (std::is_signed_v<T>) + return static_cast<int64_t>(f); + else + return static_cast<uint64_t>(f); + } + + template <typename Integral> + constexpr static void wide_integer_from_builtin(integer<Bits, Signed> & self, Integral rhs) noexcept + { + static_assert(sizeof(Integral) <= sizeof(base_type)); + + self.items[0] = _impl::to_Integral(rhs); + + if constexpr (std::is_signed_v<Integral>) + { + if (rhs < 0) + { + for (size_t i = 1; i < item_count; ++i) + self.items[i] = -1; + return; + } + } + + for (size_t i = 1; i < item_count; ++i) + self.items[i] = 0; + } + + template <typename TupleLike, size_t i = 0> + constexpr static void wide_integer_from_tuple_like(integer<Bits, Signed> & self, const TupleLike & tuple) noexcept + { + if constexpr (i < item_count) + { + if constexpr (i < std::tuple_size_v<TupleLike>) + self.items[i] = std::get<i>(tuple); + else + self.items[i] = 0; + wide_integer_from_tuple_like<TupleLike, i + 1>(self, tuple); + } + } + + /** + * N.B. t is constructed from double, so max(t) = max(double) ~ 2^310 + * the recursive call happens when t / 2^64 > 2^64, so there won't be more than 5 of them. + * + * t = a1 * max_int + b1, a1 > max_int, b1 < max_int + * a1 = a2 * max_int + b2, a2 > max_int, b2 < max_int + * a_(n - 1) = a_n * max_int + b2, a_n <= max_int <- base case. + */ + template <class T> + constexpr static void set_multiplier(integer<Bits, Signed> & self, T t) noexcept + { + constexpr uint64_t max_int = std::numeric_limits<uint64_t>::max(); + + /// Implementation specific behaviour on overflow (if we don't check here, stack overflow will triggered in bigint_cast). + if (!std::isfinite(t)) + { + self = 0; + return; + } + + const T alpha = t / static_cast<T>(max_int); + + if (alpha <= static_cast<T>(max_int)) + self = static_cast<uint64_t>(alpha); + else // max(double) / 2^64 will surely contain less than 52 precision bits, so speed up computations. + set_multiplier<double>(self, alpha); + + self *= max_int; + self += static_cast<uint64_t>(t - floor(alpha) * static_cast<T>(max_int)); // += b_i + } + + constexpr static void wide_integer_from_builtin(integer<Bits, Signed> & self, double rhs) noexcept + { + constexpr int64_t max_int = std::numeric_limits<int64_t>::max(); + constexpr int64_t min_int = std::numeric_limits<int64_t>::lowest(); + + /// There are values in int64 that have more than 53 significant bits (in terms of double + /// representation). Such values, being promoted to double, are rounded up or down. If they are rounded up, + /// the result may not fit in 64 bits. + /// The example of such a number is 9.22337e+18. + /// As to_Integral does a static_cast to int64_t, it may result in UB. + /// The necessary check here is that long double has enough significant (mantissa) bits to store the + /// int64_t max value precisely. + + // TODO Be compatible with Apple aarch64 +#if not (defined(__APPLE__) && defined(__aarch64__)) + static_assert(LDBL_MANT_DIG >= 64, + "On your system long double has less than 64 precision bits, " + "which may result in UB when initializing double from int64_t"); +#endif + + if (rhs > static_cast<long double>(min_int) && rhs < static_cast<long double>(max_int)) + { + self = static_cast<int64_t>(rhs); + return; + } + + const long double rhs_long_double = (static_cast<long double>(rhs) < 0) + ? -static_cast<long double>(rhs) + : rhs; + + set_multiplier(self, rhs_long_double); + + if (rhs < 0) + self = -self; + } + + template <size_t Bits2, typename Signed2> + constexpr static void + wide_integer_from_wide_integer(integer<Bits, Signed> & self, const integer<Bits2, Signed2> & rhs) noexcept + { + constexpr const unsigned min_bits = (Bits < Bits2) ? Bits : Bits2; + constexpr const unsigned to_copy = min_bits / base_bits; + + for (unsigned i = 0; i < to_copy; ++i) + self.items[i] = rhs.items[i]; + + if constexpr (Bits > Bits2) + { + if constexpr (std::is_signed_v<Signed2>) + { + if (rhs < 0) + { + for (unsigned i = to_copy; i < item_count; ++i) + self.items[i] = -1; + return; + } + } + + for (unsigned i = to_copy; i < item_count; ++i) + self.items[i] = 0; + } + } + + template <typename T> + constexpr static bool should_keep_size() + { + return sizeof(T) <= byte_count; + } + + constexpr static integer<Bits, Signed> shift_left(const integer<Bits, Signed> & rhs, unsigned n) noexcept + { + integer<Bits, Signed> lhs; + unsigned items_shift = n / base_bits; + + if (unsigned bit_shift = n % base_bits) + { + unsigned overflow_shift = base_bits - bit_shift; + + lhs.items[big(0)] = rhs.items[big(items_shift)] << bit_shift; + for (unsigned i = 1; i < item_count - items_shift; ++i) + { + lhs.items[big(i - 1)] |= rhs.items[big(items_shift + i)] >> overflow_shift; + lhs.items[big(i)] = rhs.items[big(items_shift + i)] << bit_shift; + } + } + else + { + for (unsigned i = 0; i < item_count - items_shift; ++i) + lhs.items[big(i)] = rhs.items[big(items_shift + i)]; + } + + for (unsigned i = 0; i < items_shift; ++i) + lhs.items[little(i)] = 0; + return lhs; + } + + constexpr static integer<Bits, Signed> shift_right(const integer<Bits, Signed> & rhs, unsigned n) noexcept + { + integer<Bits, Signed> lhs; + unsigned items_shift = n / base_bits; + unsigned bit_shift = n % base_bits; + + if (bit_shift) + { + unsigned overflow_shift = base_bits - bit_shift; + + lhs.items[little(0)] = rhs.items[little(items_shift)] >> bit_shift; + for (unsigned i = 1; i < item_count - items_shift; ++i) + { + lhs.items[little(i - 1)] |= rhs.items[little(items_shift + i)] << overflow_shift; + lhs.items[little(i)] = rhs.items[little(items_shift + i)] >> bit_shift; + } + } + else + { + for (unsigned i = 0; i < item_count - items_shift; ++i) + lhs.items[little(i)] = rhs.items[little(items_shift + i)]; + } + + if (is_negative(rhs)) + { + if (bit_shift) + lhs.items[big(items_shift)] |= std::numeric_limits<base_type>::max() << (base_bits - bit_shift); + + for (unsigned i = 0; i < items_shift; ++i) + lhs.items[big(i)] = std::numeric_limits<base_type>::max(); + } + else + { + for (unsigned i = 0; i < items_shift; ++i) + lhs.items[big(i)] = 0; + } + + return lhs; + } + +private: + template <typename T> + constexpr static base_type get_item(const T & x, unsigned idx) + { + if constexpr (IsWideInteger<T>::value) + { + if (idx < T::_impl::item_count) + return x.items[idx]; + return 0; + } + else + { + if constexpr (sizeof(T) <= sizeof(base_type)) + { + if (0 == idx) + return x; + } + else if (idx * sizeof(base_type) < sizeof(T)) + return x >> (idx * base_bits); // & std::numeric_limits<base_type>::max() + return 0; + } + } + + template <typename T> + constexpr static integer<Bits, Signed> + minus(const integer<Bits, Signed> & lhs, T rhs) + { + constexpr const unsigned rhs_items = (sizeof(T) > sizeof(base_type)) ? (sizeof(T) / sizeof(base_type)) : 1; + constexpr const unsigned op_items = (item_count < rhs_items) ? item_count : rhs_items; + + integer<Bits, Signed> res(lhs); + bool underflows[item_count] = {}; + + for (unsigned i = 0; i < op_items; ++i) + { + base_type rhs_item = get_item(rhs, i); + base_type & res_item = res.items[little(i)]; + + underflows[i] = res_item < rhs_item; + res_item -= rhs_item; + } + + for (unsigned i = 1; i < item_count; ++i) + { + if (underflows[i - 1]) + { + base_type & res_item = res.items[little(i)]; + if (res_item == 0) + underflows[i] = true; + --res_item; + } + } + + return res; + } + + template <typename T> + constexpr static integer<Bits, Signed> + plus(const integer<Bits, Signed> & lhs, T rhs) + { + constexpr const unsigned rhs_items = (sizeof(T) > sizeof(base_type)) ? (sizeof(T) / sizeof(base_type)) : 1; + constexpr const unsigned op_items = (item_count < rhs_items) ? item_count : rhs_items; + + integer<Bits, Signed> res(lhs); + bool overflows[item_count] = {}; + + for (unsigned i = 0; i < op_items; ++i) + { + base_type rhs_item = get_item(rhs, i); + base_type & res_item = res.items[little(i)]; + + res_item += rhs_item; + overflows[i] = res_item < rhs_item; + } + + for (unsigned i = 1; i < item_count; ++i) + { + if (overflows[i - 1]) + { + base_type & res_item = res.items[little(i)]; + ++res_item; + if (res_item == 0) + overflows[i] = true; + } + } + + return res; + } + + template <typename T> + constexpr static integer<Bits, Signed> + multiply(const integer<Bits, Signed> & lhs, const T & rhs) + { + if constexpr (Bits == 256 && sizeof(base_type) == 8) + { + /// @sa https://github.com/abseil/abseil-cpp/blob/master/absl/numeric/int128.h + using HalfType = unsigned __int128; + + HalfType a01 = (HalfType(lhs.items[little(1)]) << 64) + lhs.items[little(0)]; + HalfType a23 = (HalfType(lhs.items[little(3)]) << 64) + lhs.items[little(2)]; + HalfType a0 = lhs.items[little(0)]; + HalfType a1 = lhs.items[little(1)]; + + HalfType b01 = rhs; + uint64_t b0 = b01; + uint64_t b1 = 0; + HalfType b23 = 0; + if constexpr (sizeof(T) > 8) + b1 = b01 >> 64; + if constexpr (sizeof(T) > 16) + b23 = (HalfType(rhs.items[little(3)]) << 64) + rhs.items[little(2)]; + + HalfType r23 = a23 * b01 + a01 * b23 + a1 * b1; + HalfType r01 = a0 * b0; + HalfType r12 = (r01 >> 64) + (r23 << 64); + HalfType r12_x = a1 * b0; + + integer<Bits, Signed> res; + res.items[little(0)] = r01; + res.items[little(3)] = r23 >> 64; + + if constexpr (sizeof(T) > 8) + { + HalfType r12_y = a0 * b1; + r12_x += r12_y; + if (r12_x < r12_y) + ++res.items[little(3)]; + } + + r12 += r12_x; + if (r12 < r12_x) + ++res.items[little(3)]; + + res.items[little(1)] = r12; + res.items[little(2)] = r12 >> 64; + return res; + } + else if constexpr (Bits == 128 && sizeof(base_type) == 8) + { + using CompilerUInt128 = unsigned __int128; + CompilerUInt128 a = (CompilerUInt128(lhs.items[1]) << 64) + lhs.items[0]; + CompilerUInt128 b = (CompilerUInt128(rhs.items[1]) << 64) + rhs.items[0]; + CompilerUInt128 c = a * b; + integer<Bits, Signed> res; + res.items[0] = c; + res.items[1] = c >> 64; + return res; + } + else + { + integer<Bits, Signed> res{}; +#if 1 + integer<Bits, Signed> lhs2 = plus(lhs, shift_left(lhs, 1)); + integer<Bits, Signed> lhs3 = plus(lhs2, shift_left(lhs, 2)); +#endif + for (unsigned i = 0; i < item_count; ++i) + { + base_type rhs_item = get_item(rhs, i); + unsigned pos = i * base_bits; + + while (rhs_item) + { +#if 1 /// optimization + if ((rhs_item & 0x7) == 0x7) + { + res = plus(res, shift_left(lhs3, pos)); + rhs_item >>= 3; + pos += 3; + continue; + } + + if ((rhs_item & 0x3) == 0x3) + { + res = plus(res, shift_left(lhs2, pos)); + rhs_item >>= 2; + pos += 2; + continue; + } +#endif + if (rhs_item & 1) + res = plus(res, shift_left(lhs, pos)); + + rhs_item >>= 1; + ++pos; + } + } + + return res; + } + } + +public: + constexpr static integer<Bits, Signed> operator_unary_tilda(const integer<Bits, Signed> & lhs) noexcept + { + integer<Bits, Signed> res; + + for (unsigned i = 0; i < item_count; ++i) + res.items[any(i)] = ~lhs.items[any(i)]; + return res; + } + + constexpr static integer<Bits, Signed> + operator_unary_minus(const integer<Bits, Signed> & lhs) noexcept(std::is_same_v<Signed, unsigned>) + { + return plus(operator_unary_tilda(lhs), 1); + } + + template <typename T> + constexpr static auto operator_plus(const integer<Bits, Signed> & lhs, const T & rhs) noexcept(std::is_same_v<Signed, unsigned>) + { + if constexpr (should_keep_size<T>()) + { + if (is_negative(rhs)) + return minus(lhs, -rhs); + else + return plus(lhs, rhs); + } + else + { + static_assert(IsWideInteger<T>::value); + return std::common_type_t<integer<Bits, Signed>, integer<T::_impl::_bits, Signed>>::_impl::operator_plus( + integer<T::_impl::_bits, Signed>(lhs), rhs); + } + } + + template <typename T> + constexpr static auto operator_minus(const integer<Bits, Signed> & lhs, const T & rhs) noexcept(std::is_same_v<Signed, unsigned>) + { + if constexpr (should_keep_size<T>()) + { + if (is_negative(rhs)) + return plus(lhs, -rhs); + else + return minus(lhs, rhs); + } + else + { + static_assert(IsWideInteger<T>::value); + return std::common_type_t<integer<Bits, Signed>, integer<T::_impl::_bits, Signed>>::_impl::operator_minus( + integer<T::_impl::_bits, Signed>(lhs), rhs); + } + } + + template <typename T> + constexpr static auto operator_star(const integer<Bits, Signed> & lhs, const T & rhs) + { + if constexpr (should_keep_size<T>()) + { + integer<Bits, Signed> res; + + if constexpr (std::is_signed_v<Signed>) + { + res = multiply((is_negative(lhs) ? make_positive(lhs) : lhs), + (is_negative(rhs) ? make_positive(rhs) : rhs)); + } + else + { + res = multiply(lhs, (is_negative(rhs) ? make_positive(rhs) : rhs)); + } + + if (std::is_same_v<Signed, signed> && is_negative(lhs) != is_negative(rhs)) + res = operator_unary_minus(res); + + return res; + } + else + { + static_assert(IsWideInteger<T>::value); + return std::common_type_t<integer<Bits, Signed>, T>::_impl::operator_star(T(lhs), rhs); + } + } + + template <typename T> + constexpr static bool operator_greater(const integer<Bits, Signed> & lhs, const T & rhs) noexcept + { + if constexpr (should_keep_size<T>()) + { + if (std::numeric_limits<T>::is_signed && (is_negative(lhs) != is_negative(rhs))) + return is_negative(rhs); + + for (unsigned i = 0; i < item_count; ++i) + { + base_type rhs_item = get_item(rhs, big(i)); + + if (lhs.items[big(i)] != rhs_item) + return lhs.items[big(i)] > rhs_item; + } + + return false; + } + else + { + static_assert(IsWideInteger<T>::value); + return std::common_type_t<integer<Bits, Signed>, T>::_impl::operator_greater(T(lhs), rhs); + } + } + + template <typename T> + constexpr static bool operator_less(const integer<Bits, Signed> & lhs, const T & rhs) noexcept + { + if constexpr (should_keep_size<T>()) + { + if (std::numeric_limits<T>::is_signed && (is_negative(lhs) != is_negative(rhs))) + return is_negative(lhs); + + for (unsigned i = 0; i < item_count; ++i) + { + base_type rhs_item = get_item(rhs, big(i)); + + if (lhs.items[big(i)] != rhs_item) + return lhs.items[big(i)] < rhs_item; + } + + return false; + } + else + { + static_assert(IsWideInteger<T>::value); + return std::common_type_t<integer<Bits, Signed>, T>::_impl::operator_less(T(lhs), rhs); + } + } + + template <typename T> + constexpr static bool operator_eq(const integer<Bits, Signed> & lhs, const T & rhs) noexcept + { + if constexpr (should_keep_size<T>()) + { + for (unsigned i = 0; i < item_count; ++i) + { + base_type rhs_item = get_item(rhs, any(i)); + + if (lhs.items[any(i)] != rhs_item) + return false; + } + + return true; + } + else + { + static_assert(IsWideInteger<T>::value); + return std::common_type_t<integer<Bits, Signed>, T>::_impl::operator_eq(T(lhs), rhs); + } + } + + template <typename T> + constexpr static auto operator_pipe(const integer<Bits, Signed> & lhs, const T & rhs) noexcept + { + if constexpr (should_keep_size<T>()) + { + integer<Bits, Signed> res; + + for (unsigned i = 0; i < item_count; ++i) + res.items[little(i)] = lhs.items[little(i)] | get_item(rhs, i); + return res; + } + else + { + static_assert(IsWideInteger<T>::value); + return std::common_type_t<integer<Bits, Signed>, T>::_impl::operator_pipe(T(lhs), rhs); + } + } + + template <typename T> + constexpr static auto operator_amp(const integer<Bits, Signed> & lhs, const T & rhs) noexcept + { + if constexpr (should_keep_size<T>()) + { + integer<Bits, Signed> res; + + for (unsigned i = 0; i < item_count; ++i) + res.items[little(i)] = lhs.items[little(i)] & get_item(rhs, i); + return res; + } + else + { + static_assert(IsWideInteger<T>::value); + return std::common_type_t<integer<Bits, Signed>, T>::_impl::operator_amp(T(lhs), rhs); + } + } + + template <typename T> + constexpr static bool is_zero(const T & x) + { + bool is_zero = true; + for (auto item : x.items) + { + if (item != 0) + { + is_zero = false; + break; + } + } + return is_zero; + } + + /// returns quotient as result and remainder in numerator. + template <size_t Bits2> + constexpr static integer<Bits2, unsigned> divide(integer<Bits2, unsigned> & numerator, integer<Bits2, unsigned> denominator) + { + static_assert(std::is_unsigned_v<Signed>); + + if constexpr (Bits == 128 && sizeof(base_type) == 8) + { + using CompilerUInt128 = unsigned __int128; + + CompilerUInt128 a = (CompilerUInt128(numerator.items[1]) << 64) + numerator.items[0]; + CompilerUInt128 b = (CompilerUInt128(denominator.items[1]) << 64) + denominator.items[0]; + CompilerUInt128 c = a / b; + + integer<Bits, Signed> res; + res.items[0] = c; + res.items[1] = c >> 64; + + CompilerUInt128 remainder = a - b * c; + numerator.items[0] = remainder; + numerator.items[1] = remainder >> 64; + + return res; + } + + if (is_zero(denominator)) + throw std::runtime_error("Division by zero"); + + integer<Bits2, unsigned> x = 1; + integer<Bits2, unsigned> quotient = 0; + + while (!operator_greater(denominator, numerator) && is_zero(operator_amp(shift_right(denominator, Bits2 - 1), 1))) + { + x = shift_left(x, 1); + denominator = shift_left(denominator, 1); + } + + while (!is_zero(x)) + { + if (!operator_greater(denominator, numerator)) + { + numerator = operator_minus(numerator, denominator); + quotient = operator_pipe(quotient, x); + } + + x = shift_right(x, 1); + denominator = shift_right(denominator, 1); + } + + return quotient; + } + + template <typename T> + constexpr static auto operator_slash(const integer<Bits, Signed> & lhs, const T & rhs) + { + if constexpr (should_keep_size<T>()) + { + integer<Bits, unsigned> numerator = make_positive(lhs); + integer<Bits, unsigned> denominator = make_positive(integer<Bits, Signed>(rhs)); + integer<Bits, unsigned> quotient = integer<Bits, unsigned>::_impl::divide(numerator, std::move(denominator)); + + if (std::is_same_v<Signed, signed> && is_negative(rhs) != is_negative(lhs)) + quotient = operator_unary_minus(quotient); + return quotient; + } + else + { + static_assert(IsWideInteger<T>::value); + return std::common_type_t<integer<Bits, Signed>, integer<T::_impl::_bits, Signed>>::operator_slash(T(lhs), rhs); + } + } + + template <typename T> + constexpr static auto operator_percent(const integer<Bits, Signed> & lhs, const T & rhs) + { + if constexpr (should_keep_size<T>()) + { + integer<Bits, unsigned> remainder = make_positive(lhs); + integer<Bits, unsigned> denominator = make_positive(integer<Bits, Signed>(rhs)); + integer<Bits, unsigned>::_impl::divide(remainder, std::move(denominator)); + + if (std::is_same_v<Signed, signed> && is_negative(lhs)) + remainder = operator_unary_minus(remainder); + return remainder; + } + else + { + static_assert(IsWideInteger<T>::value); + return std::common_type_t<integer<Bits, Signed>, integer<T::_impl::_bits, Signed>>::operator_percent(T(lhs), rhs); + } + } + + // ^ + template <typename T> + constexpr static auto operator_circumflex(const integer<Bits, Signed> & lhs, const T & rhs) noexcept + { + if constexpr (should_keep_size<T>()) + { + integer<Bits, Signed> t(rhs); + integer<Bits, Signed> res = lhs; + + for (unsigned i = 0; i < item_count; ++i) + res.items[any(i)] ^= t.items[any(i)]; + return res; + } + else + { + static_assert(IsWideInteger<T>::value); + return T::operator_circumflex(T(lhs), rhs); + } + } + + constexpr static integer<Bits, Signed> from_str(const char * c) + { + integer<Bits, Signed> res = 0; + + bool is_neg = std::is_same_v<Signed, signed> && *c == '-'; + if (is_neg) + ++c; + + if (*c == '0' && (*(c + 1) == 'x' || *(c + 1) == 'X')) + { // hex + ++c; + ++c; + while (*c) + { + if (*c >= '0' && *c <= '9') + { + res = multiply(res, 16U); + res = plus(res, *c - '0'); + ++c; + } + else if (*c >= 'a' && *c <= 'f') + { + res = multiply(res, 16U); + res = plus(res, *c - 'a' + 10U); + ++c; + } + else if (*c >= 'A' && *c <= 'F') + { // tolower must be used, but it is not constexpr + res = multiply(res, 16U); + res = plus(res, *c - 'A' + 10U); + ++c; + } + else + throw std::runtime_error("Invalid char from"); + } + } + else + { // dec + while (*c) + { + if (*c < '0' || *c > '9') + throw std::runtime_error("Invalid char from"); + + res = multiply(res, 10U); + res = plus(res, *c - '0'); + ++c; + } + } + + if (is_neg) + res = operator_unary_minus(res); + + return res; + } +}; + +// Members + +template <size_t Bits, typename Signed> +template <typename T> +constexpr integer<Bits, Signed>::integer(T rhs) noexcept + : items{} +{ + if constexpr (IsWideInteger<T>::value) + _impl::wide_integer_from_wide_integer(*this, rhs); + else if constexpr (IsTupleLike<T>::value) + _impl::wide_integer_from_tuple_like(*this, rhs); + else + _impl::wide_integer_from_builtin(*this, rhs); +} + +template <size_t Bits, typename Signed> +template <typename T> +constexpr integer<Bits, Signed>::integer(std::initializer_list<T> il) noexcept + : items{} +{ + if (il.size() == 1) + { + if constexpr (IsWideInteger<T>::value) + _impl::wide_integer_from_wide_integer(*this, *il.begin()); + else if constexpr (IsTupleLike<T>::value) + _impl::wide_integer_from_tuple_like(*this, *il.begin()); + else + _impl::wide_integer_from_builtin(*this, *il.begin()); + } + else if (il.size() == 0) + { + _impl::wide_integer_from_builtin(*this, 0); + } + else + { + auto it = il.begin(); + for (size_t i = 0; i < _impl::item_count; ++i) + if (it < il.end()) + items[i] = *it; + } +} + +template <size_t Bits, typename Signed> +template <size_t Bits2, typename Signed2> +constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator=(const integer<Bits2, Signed2> & rhs) noexcept +{ + _impl::wide_integer_from_wide_integer(*this, rhs); + return *this; +} + +template <size_t Bits, typename Signed> +template <typename T> +constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator=(T rhs) noexcept +{ + if constexpr (IsTupleLike<T>::value) + _impl::wide_integer_from_tuple_like(*this, rhs); + else + _impl::wide_integer_from_builtin(*this, rhs); + return *this; +} + +template <size_t Bits, typename Signed> +template <typename T> +constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator*=(const T & rhs) +{ + *this = *this * rhs; + return *this; +} + +template <size_t Bits, typename Signed> +template <typename T> +constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator/=(const T & rhs) +{ + *this = *this / rhs; + return *this; +} + +template <size_t Bits, typename Signed> +template <typename T> +constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator+=(const T & rhs) noexcept(std::is_same_v<Signed, unsigned>) +{ + *this = *this + rhs; + return *this; +} + +template <size_t Bits, typename Signed> +template <typename T> +constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator-=(const T & rhs) noexcept(std::is_same_v<Signed, unsigned>) +{ + *this = *this - rhs; + return *this; +} + +template <size_t Bits, typename Signed> +template <typename T> +constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator%=(const T & rhs) +{ + *this = *this % rhs; + return *this; +} + +template <size_t Bits, typename Signed> +template <typename T> +constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator&=(const T & rhs) noexcept +{ + *this = *this & rhs; + return *this; +} + +template <size_t Bits, typename Signed> +template <typename T> +constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator|=(const T & rhs) noexcept +{ + *this = *this | rhs; + return *this; +} + +template <size_t Bits, typename Signed> +template <typename T> +constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator^=(const T & rhs) noexcept +{ + *this = *this ^ rhs; + return *this; +} + +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator<<=(int n) noexcept +{ + if (static_cast<size_t>(n) >= Bits) + *this = 0; + else if (n > 0) + *this = _impl::shift_left(*this, n); + return *this; +} + +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator>>=(int n) noexcept +{ + if (static_cast<size_t>(n) >= Bits) + { + if (_impl::is_negative(*this)) + *this = -1; + else + *this = 0; + } + else if (n > 0) + *this = _impl::shift_right(*this, n); + return *this; +} + +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator++() noexcept(std::is_same_v<Signed, unsigned>) +{ + *this = _impl::operator_plus(*this, 1); + return *this; +} + +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed> integer<Bits, Signed>::operator++(int) noexcept(std::is_same_v<Signed, unsigned>) +{ + auto tmp = *this; + *this = _impl::operator_plus(*this, 1); + return tmp; +} + +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator--() noexcept(std::is_same_v<Signed, unsigned>) +{ + *this = _impl::operator_minus(*this, 1); + return *this; +} + +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed> integer<Bits, Signed>::operator--(int) noexcept(std::is_same_v<Signed, unsigned>) +{ + auto tmp = *this; + *this = _impl::operator_minus(*this, 1); + return tmp; +} + +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed>::operator bool() const noexcept +{ + return !_impl::operator_eq(*this, 0); +} + +template <size_t Bits, typename Signed> +template <class T, class> +constexpr integer<Bits, Signed>::operator T() const noexcept +{ + static_assert(std::numeric_limits<T>::is_integer); + + /// NOTE: memcpy will suffice, but unfortunately, this function is constexpr. + + using UnsignedT = std::make_unsigned_t<T>; + + UnsignedT res{}; + for (unsigned i = 0; i < _impl::item_count && i < (sizeof(T) + sizeof(base_type) - 1) / sizeof(base_type); ++i) + res += UnsignedT(items[i]) << (sizeof(base_type) * 8 * i); + + return res; +} + +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed>::operator long double() const noexcept +{ + if (_impl::operator_eq(*this, 0)) + return 0; + + integer<Bits, Signed> tmp = *this; + if (_impl::is_negative(*this)) + tmp = -tmp; + + long double res = 0; + for (unsigned i = 0; i < _impl::item_count; ++i) + { + long double t = res; + res *= std::numeric_limits<base_type>::max(); + res += t; + res += tmp.items[_impl::big(i)]; + } + + if (_impl::is_negative(*this)) + res = -res; + + return res; +} + +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed>::operator double() const noexcept +{ + return static_cast<long double>(*this); +} + +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed>::operator float() const noexcept +{ + return static_cast<long double>(*this); +} + +// Unary operators +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed> operator~(const integer<Bits, Signed> & lhs) noexcept +{ + return integer<Bits, Signed>::_impl::operator_unary_tilda(lhs); +} + +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed> operator-(const integer<Bits, Signed> & lhs) noexcept(std::is_same_v<Signed, unsigned>) +{ + return integer<Bits, Signed>::_impl::operator_unary_minus(lhs); +} + +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed> operator+(const integer<Bits, Signed> & lhs) noexcept(std::is_same_v<Signed, unsigned>) +{ + return lhs; +} + +#define CT(x) \ + std::common_type_t<std::decay_t<decltype(rhs)>, std::decay_t<decltype(lhs)>> { x } + +// Binary operators +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr +operator*(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs) +{ + return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_star(lhs, rhs); +} + +template <typename Arithmetic, typename Arithmetic2, class> +std::common_type_t<Arithmetic, Arithmetic2> constexpr operator*(const Arithmetic & lhs, const Arithmetic2 & rhs) +{ + return CT(lhs) * CT(rhs); +} + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr +operator/(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs) +{ + return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_slash(lhs, rhs); +} +template <typename Arithmetic, typename Arithmetic2, class> +std::common_type_t<Arithmetic, Arithmetic2> constexpr operator/(const Arithmetic & lhs, const Arithmetic2 & rhs) +{ + return CT(lhs) / CT(rhs); +} + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr +operator+(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs) +{ + return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_plus(lhs, rhs); +} +template <typename Arithmetic, typename Arithmetic2, class> +std::common_type_t<Arithmetic, Arithmetic2> constexpr operator+(const Arithmetic & lhs, const Arithmetic2 & rhs) +{ + return CT(lhs) + CT(rhs); +} + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr +operator-(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs) +{ + return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_minus(lhs, rhs); +} +template <typename Arithmetic, typename Arithmetic2, class> +std::common_type_t<Arithmetic, Arithmetic2> constexpr operator-(const Arithmetic & lhs, const Arithmetic2 & rhs) +{ + return CT(lhs) - CT(rhs); +} + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr +operator%(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs) +{ + return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_percent(lhs, rhs); +} +template <typename Integral, typename Integral2, class> +std::common_type_t<Integral, Integral2> constexpr operator%(const Integral & lhs, const Integral2 & rhs) +{ + return CT(lhs) % CT(rhs); +} + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr +operator&(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs) +{ + return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_amp(lhs, rhs); +} +template <typename Integral, typename Integral2, class> +std::common_type_t<Integral, Integral2> constexpr operator&(const Integral & lhs, const Integral2 & rhs) +{ + return CT(lhs) & CT(rhs); +} + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr +operator|(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs) +{ + return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_pipe(lhs, rhs); +} +template <typename Integral, typename Integral2, class> +std::common_type_t<Integral, Integral2> constexpr operator|(const Integral & lhs, const Integral2 & rhs) +{ + return CT(lhs) | CT(rhs); +} + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr +operator^(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs) +{ + return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_circumflex(lhs, rhs); +} +template <typename Integral, typename Integral2, class> +std::common_type_t<Integral, Integral2> constexpr operator^(const Integral & lhs, const Integral2 & rhs) +{ + return CT(lhs) ^ CT(rhs); +} + +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed> operator<<(const integer<Bits, Signed> & lhs, int n) noexcept +{ + if (static_cast<size_t>(n) >= Bits) + return integer<Bits, Signed>(0); + if (n <= 0) + return lhs; + return integer<Bits, Signed>::_impl::shift_left(lhs, n); +} +template <size_t Bits, typename Signed> +constexpr integer<Bits, Signed> operator>>(const integer<Bits, Signed> & lhs, int n) noexcept +{ + if (static_cast<size_t>(n) >= Bits) + return integer<Bits, Signed>(0); + if (n <= 0) + return lhs; + return integer<Bits, Signed>::_impl::shift_right(lhs, n); +} + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +constexpr bool operator<(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs) +{ + return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_less(lhs, rhs); +} +template <typename Arithmetic, typename Arithmetic2, class> +constexpr bool operator<(const Arithmetic & lhs, const Arithmetic2 & rhs) +{ + return CT(lhs) < CT(rhs); +} + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +constexpr bool operator>(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs) +{ + return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_greater(lhs, rhs); +} +template <typename Arithmetic, typename Arithmetic2, class> +constexpr bool operator>(const Arithmetic & lhs, const Arithmetic2 & rhs) +{ + return CT(lhs) > CT(rhs); +} + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +constexpr bool operator<=(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs) +{ + return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_less(lhs, rhs) + || std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_eq(lhs, rhs); +} +template <typename Arithmetic, typename Arithmetic2, class> +constexpr bool operator<=(const Arithmetic & lhs, const Arithmetic2 & rhs) +{ + return CT(lhs) <= CT(rhs); +} + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +constexpr bool operator>=(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs) +{ + return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_greater(lhs, rhs) + || std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_eq(lhs, rhs); +} +template <typename Arithmetic, typename Arithmetic2, class> +constexpr bool operator>=(const Arithmetic & lhs, const Arithmetic2 & rhs) +{ + return CT(lhs) >= CT(rhs); +} + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +constexpr bool operator==(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs) +{ + return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_eq(lhs, rhs); +} +template <typename Arithmetic, typename Arithmetic2, class> +constexpr bool operator==(const Arithmetic & lhs, const Arithmetic2 & rhs) +{ + return CT(lhs) == CT(rhs); +} + +template <size_t Bits, typename Signed, size_t Bits2, typename Signed2> +constexpr bool operator!=(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs) +{ + return !std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_eq(lhs, rhs); +} +template <typename Arithmetic, typename Arithmetic2, class> +constexpr bool operator!=(const Arithmetic & lhs, const Arithmetic2 & rhs) +{ + return CT(lhs) != CT(rhs); +} + +#undef CT + +} + +namespace std +{ + +template <size_t Bits, typename Signed> +struct hash<CH::wide::integer<Bits, Signed>> +{ + std::size_t operator()(const CH::wide::integer<Bits, Signed> & lhs) const + { + static_assert(Bits % (sizeof(size_t) * 8) == 0); + + const auto * ptr = reinterpret_cast<const size_t *>(lhs.items); + unsigned count = Bits / (sizeof(size_t) * 8); + + size_t res = 0; + for (unsigned i = 0; i < count; ++i) + res ^= ptr[i]; + return res; + } +}; + +} diff --git a/ydb/library/yql/public/udf/udfs_exports.exports b/ydb/library/yql/public/udf/udfs_exports.exports new file mode 100644 index 00000000000..c3af1d6045d --- /dev/null +++ b/ydb/library/yql/public/udf/udfs_exports.exports @@ -0,0 +1,4 @@ +C Register +C AbiVersion +C SetBackTraceCallback +C BindSymbols |