123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397 |
- /*************************************************************************
- *
- * Copyright 2016 Realm Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- **************************************************************************/
- #ifndef REALM_UTILITIES_HPP
- #define REALM_UTILITIES_HPP
- #include <cstdint>
- #include <cstdlib>
- #include <cstdlib> // size_t
- #include <cstdio>
- #include <algorithm>
- #include <functional>
- #include <time.h>
- #ifdef _WIN32
- #include <WinSock2.h>
- #include <intrin.h>
- #include <BaseTsd.h>
- #if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED)
- typedef SSIZE_T ssize_t;
- #define _SSIZE_T_
- #define _SSIZE_T_DEFINED
- #endif
- #endif // _WIN32
- #include <realm/util/features.h>
- #include <realm/util/assert.hpp>
- #include <realm/util/safe_int_ops.hpp>
- // GCC defines __i386__ and __x86_64__
- #if (defined(__X86__) || defined(__i386__) || defined(i386) || defined(_M_IX86) || defined(__386__) || \
- defined(__x86_64__) || defined(_M_X64))
- #define REALM_X86_OR_X64
- #define REALM_X86_OR_X64_TRUE true
- #else
- #define REALM_X86_OR_X64_TRUE false
- #endif
- // GCC defines __arm__
- #ifdef __arm__
- #define REALM_ARCH_ARM
- #endif
- #if defined _LP64 || defined __LP64__ || defined __64BIT__ || defined _ADDR64 || defined _WIN64 || \
- defined __arch64__ || (defined(__WORDSIZE) && __WORDSIZE == 64) || (defined __sparc && defined __sparcv9) || \
- defined __x86_64 || defined __amd64 || defined __x86_64__ || defined _M_X64 || defined _M_IA64 || \
- defined __ia64 || defined __IA64__
- #define REALM_PTR_64
- #endif
- #if defined(REALM_PTR_64) && defined(REALM_X86_OR_X64) && !REALM_WATCHOS
- #define REALM_COMPILER_SSE // Compiler supports SSE 4.2 through __builtin_ accessors or back-end assembler
- #define REALM_COMPILER_AVX
- #endif
- namespace realm {
- using StringCompareCallback = std::function<bool(const char* string1, const char* string2)>;
- extern signed char sse_support;
- extern signed char avx_support;
- template <int version>
- REALM_FORCEINLINE bool sseavx()
- {
- /*
- Return whether or not SSE 3.0 (if version = 30) or 4.2 (for version = 42) is supported. Return value
- is based on the CPUID instruction.
- sse_support = -1: No SSE support
- sse_support = 0: SSE3
- sse_support = 1: SSE42
- avx_support = -1: No AVX support
- avx_support = 0: AVX1 supported
- sse_support = 1: AVX2 supported (not yet implemented for detection in our cpuid_init(), todo)
- This lets us test very rapidly at runtime because we just need 1 compare instruction (with 0) to test both for
- SSE 3 and 4.2 by caller (compiler optimizes if calls are concecutive), and can decide branch with ja/jl/je because
- sse_support is signed type. Also, 0 requires no immediate operand. Same for AVX.
- We runtime-initialize sse_support in a constructor of a static variable which is not guaranteed to be called
- prior to cpu_sse(). So we compile-time initialize sse_support to -2 as fallback.
- */
- static_assert(version == 1 || version == 2 || version == 30 || version == 42,
- "Only version == 1 (AVX), 2 (AVX2), 30 (SSE 3) and 42 (SSE 4.2) are supported for detection");
- #ifdef REALM_COMPILER_SSE
- if (version == 30)
- return (sse_support >= 0);
- else if (version == 42)
- return (sse_support > 0); // faster than == 1 (0 requres no immediate operand)
- else if (version == 1) // avx
- return (avx_support >= 0);
- else if (version == 2) // avx2
- return (avx_support > 0);
- else
- return false;
- #else
- return false;
- #endif
- }
- void cpuid_init();
- void* round_up(void* p, size_t align);
- void* round_down(void* p, size_t align);
- constexpr size_t round_up(size_t p, size_t align);
- constexpr size_t round_down(size_t p, size_t align);
- void millisleep(unsigned long milliseconds);
- #ifdef _WIN32
- int gettimeofday(struct timeval* tp, struct timezone* tzp);
- #endif
- int64_t platform_timegm(tm time);
- #ifdef REALM_SLAB_ALLOC_TUNE
- void process_mem_usage(double& vm_usage, double& resident_set);
- #endif
- // popcount
- int fast_popcount32(int32_t x);
- int fast_popcount64(int64_t x);
- uint64_t fastrand(uint64_t max = 0xffffffffffffffffULL, bool is_seed = false);
- // Class to be used when a private generator is wanted.
- // Object of this class should not be shared between threads.
- class FastRand {
- public:
- FastRand(uint64_t seed = 1)
- : m_state(seed)
- {
- }
- uint64_t operator()(uint64_t max = uint64_t(-1));
- private:
- uint64_t m_state;
- };
- // log2 - returns -1 if x==0, otherwise log2(x)
- inline int log2(size_t x)
- {
- if (x == 0)
- return -1;
- #if defined(__GNUC__)
- #ifdef REALM_PTR_64
- return 63 - __builtin_clzll(x); // returns int
- #else
- return 31 - __builtin_clz(x); // returns int
- #endif
- #elif defined(_WIN32)
- unsigned long index = 0;
- #ifdef REALM_PTR_64
- unsigned char c = _BitScanReverse64(&index, x); // outputs unsigned long
- #else
- unsigned char c = _BitScanReverse(&index, x); // outputs unsigned long
- #endif
- return static_cast<int>(index);
- #else // not __GNUC__ and not _WIN32
- int r = 0;
- while (x >>= 1) {
- r++;
- }
- return r;
- #endif
- }
- // count trailing zeros (from least-significant bit)
- inline int ctz(size_t x)
- {
- if (x == 0)
- return sizeof(x) * 8;
- #if defined(__GNUC__)
- #ifdef REALM_PTR_64
- return __builtin_ctzll(x); // returns int
- #else
- return __builtin_ctz(x); // returns int
- #endif
- #elif defined(_WIN32)
- unsigned long index = 0;
- #ifdef REALM_PTR_64
- unsigned char c = _BitScanForward64(&index, x); // outputs unsigned long
- #else
- unsigned char c = _BitScanForward(&index, x); // outputs unsigned long
- #endif
- return static_cast<int>(index);
- #else // not __GNUC__ and not _WIN32
- int r = 0;
- while (r < sizeof(size_t) * 8 && (x & 1) == 0) {
- x >>= 1;
- r++;
- }
- return r;
- #endif
- }
- // Implementation:
- // Safe cast from 64 to 32 bits on 32 bit architecture. Differs from to_ref() by not testing alignment and
- // REF-bitflag.
- inline size_t to_size_t(int_fast64_t v) noexcept
- {
- REALM_ASSERT_DEBUG(!util::int_cast_has_overflow<size_t>(v));
- return size_t(v);
- }
- template <typename ReturnType, typename OriginalType>
- ReturnType type_punning(OriginalType variable) noexcept
- {
- union Both {
- OriginalType in;
- ReturnType out;
- };
- Both both;
- both.out = ReturnType(); // Clear all bits in case ReturnType is larger than OriginalType
- both.in = variable;
- return both.out;
- }
- // Also see the comments in Array::index_string()
- enum FindRes {
- // Indicate that no results were found in the search
- FindRes_not_found,
- // Indicates a single result is found
- FindRes_single,
- // Indicates more than one result is found and they are stored in a column
- FindRes_column
- };
- enum IndexMethod {
- index_FindFirst,
- index_FindAll_nocopy,
- index_Count,
- };
- // Combined result of the index_FindAll_nocopy operation. The column returned
- // can contain results that are not matches but all matches are within the
- // returned start_ndx and end_ndx.
- struct InternalFindResult {
- // Reference to a IntegerColumn containing result rows, or a single row
- // value if the result is FindRes_single.
- int64_t payload;
- // Offset into the result column to start at.
- size_t start_ndx;
- // Offset index in the result column to end at.
- size_t end_ndx;
- };
- // realm::is_any<T, U1, U2, U3, ...> ==
- // std::is_same<T, U1>::value || std::is_same<T, U2>::value || std::is_same<T, U3>::value ...
- template <typename... T>
- struct is_any : std::false_type {
- };
- template <typename T, typename... Ts>
- struct is_any<T, T, Ts...> : std::true_type {
- };
- template <typename T, typename U, typename... Ts>
- struct is_any<T, U, Ts...> : is_any<T, Ts...> {
- };
- template <typename... Ts>
- inline constexpr bool is_any_v = is_any<Ts...>::value;
- // Use realm::safe_equal() instead of std::equal() if one of the parameters can be a null pointer.
- template <class InputIterator1, class InputIterator2>
- bool safe_equal(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2)
- {
- #if defined(_MSC_VER)
- // VS has a special check in debug mode against passing a null pointer std::equal(); it will give a warning
- // at runtime if this is observed.
- // It's uncertain if this is allowed by the C++ standard. For details, see
- // http://stackoverflow.com/questions/19120779/is-char-p-0-stdequalp-p-p-well-defined-according-to-the-c-standard.
- // So we use a safe C++14 method instead that takes two range pairs.
- size_t len = last1 - first1;
- return std::equal(first1, last1, first2, first2 + len);
- #else
- return std::equal(first1, last1, first2);
- #endif
- }
- // Use realm::safe_copy_n() instead of std::copy_n() if one of the parameters can be a null pointer. See the
- // explanation of safe_equal() above; same things apply.
- template <class InputIt, class Size, class OutputIt>
- OutputIt safe_copy_n(InputIt first, Size count, OutputIt result)
- {
- #if defined(_MSC_VER)
- // This loop and the method prototype is copy pasted
- // from "Possible implementation" on http://en.cppreference.com/w/cpp/algorithm/copy_n
- if (count > 0) {
- *result++ = *first;
- for (Size i = 1; i < count; ++i) {
- *result++ = *++first;
- }
- }
- return result;
- #else
- return std::copy_n(first, count, result);
- #endif
- }
- // Converts ascii c-locale uppercase characters to lower case,
- // leaves other char values unchanged.
- inline char toLowerAscii(char c)
- {
- if (isascii(c) && isupper(c)) {
- #if REALM_ANDROID
- return tolower(c); // _tolower is not supported on all ABI levels
- #else
- return _tolower(c);
- #endif
- }
- return c;
- }
- inline void* round_up(void* p, size_t align)
- {
- size_t r = size_t(p) % align == 0 ? 0 : align - size_t(p) % align;
- return static_cast<char*>(p) + r;
- }
- inline void* round_down(void* p, size_t align)
- {
- size_t r = size_t(p);
- return reinterpret_cast<void*>(r & ~(align - 1));
- }
- constexpr inline size_t round_up(size_t p, size_t align)
- {
- size_t r = p % align == 0 ? 0 : align - p % align;
- return p + r;
- }
- constexpr inline size_t round_down(size_t p, size_t align)
- {
- size_t r = p;
- return r & (~(align - 1));
- }
- template <class T>
- struct Wrap {
- Wrap(const T& v)
- : m_value(v)
- {
- }
- operator T() const
- {
- return m_value;
- }
- private:
- T m_value;
- };
- // PlacementDelete is intended for use with std::unique_ptr when it holds an object allocated with
- // placement new. It simply calls the object's destructor without freeing the memory.
- struct PlacementDelete {
- template <class T>
- void operator()(T* v) const
- {
- v->~T();
- }
- };
- #ifdef _WIN32
- typedef void* FileDesc;
- #else
- typedef int FileDesc;
- #endif
- } // namespace realm
- #endif // REALM_UTILITIES_HPP
|