/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef MISC_H_INCLUDED #define MISC_H_INCLUDED #include #include #include #include #include #include #include // IWYU pragma: keep // IWYU pragma: no_include <__exception/terminate.h> #include #include #include #include #include #include #include #include #include #if !defined(NO_PREFETCH) && (defined(_MSC_VER) || defined(__INTEL_COMPILER)) #include #endif #define stringify2(x) #x #define stringify(x) stringify2(x) namespace Stockfish { std::string engine_version_info(); std::string engine_info(bool to_uci = false); std::string compiler_info(); // Prefetch hint enums for explicit call-site control. enum class PrefetchRw { READ, WRITE }; // NOTE: PrefetchLoc controls locality / cache level, not whether a prefetch // is issued. In particular, PrefetchLoc::NONE maps to a non-temporal / // lowest-locality prefetch (Intel: _MM_HINT_NTA, GCC/Clang: locality = 0) // and therefore still performs a prefetch. To completely disable // prefetching, define NO_PREFETCH so that prefetch() becomes a no-op. enum class PrefetchLoc { NONE, // Non-temporal / no cache locality (still issues a prefetch) LOW, // Low locality (e.g. T2 / L2) MODERATE, // Moderate locality (e.g. T1 / L1) HIGH // High locality (e.g. T0 / closest cache) }; // Preloads the given address into cache. This is a non-blocking // function that doesn't stall the CPU waiting for data to be loaded from memory, // which can be quite slow. #ifdef NO_PREFETCH template void prefetch(const void*) {} #elif defined(_MSC_VER) || defined(__INTEL_COMPILER) constexpr int get_intel_hint(PrefetchRw rw, PrefetchLoc loc) { if (rw == PrefetchRw::WRITE) { #ifdef _MM_HINT_ET0 return _MM_HINT_ET0; #else // Fallback when write-prefetch hint is not available: use T0 return _MM_HINT_T0; #endif } switch (loc) { case PrefetchLoc::NONE : return _MM_HINT_NTA; case PrefetchLoc::LOW : return _MM_HINT_T2; case PrefetchLoc::MODERATE : return _MM_HINT_T1; case PrefetchLoc::HIGH : return _MM_HINT_T0; default : return _MM_HINT_T0; } } template void prefetch(const void* addr) { _mm_prefetch(static_cast(addr), get_intel_hint(RW, LOC)); } #else template void prefetch(const void* addr) { __builtin_prefetch(addr, static_cast(RW), static_cast(LOC)); } #endif void start_logger(const std::string& fname); size_t str_to_size_t(const std::string& s); #if defined(__linux__) struct PipeDeleter { void operator()(FILE* file) const { if (file != nullptr) { pclose(file); } } }; #endif // Reads the file as bytes. // Returns std::nullopt if the file does not exist. std::optional read_file_to_string(const std::string& path); void dbg_hit_on(bool cond, int slot = 0); void dbg_mean_of(int64_t value, int slot = 0); void dbg_stdev_of(int64_t value, int slot = 0); void dbg_extremes_of(int64_t value, int slot = 0); void dbg_correl_of(int64_t value1, int64_t value2, int slot = 0); void dbg_print(); void dbg_clear(); using TimePoint = std::chrono::milliseconds::rep; // A value in milliseconds static_assert(sizeof(TimePoint) == sizeof(int64_t), "TimePoint should be 64 bits"); inline TimePoint now() { return std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()) .count(); } inline std::vector split(std::string_view s, std::string_view delimiter) { std::vector res; if (s.empty()) return res; size_t begin = 0; for (;;) { const size_t end = s.find(delimiter, begin); if (end == std::string::npos) break; res.emplace_back(s.substr(begin, end - begin)); begin = end + delimiter.size(); } res.emplace_back(s.substr(begin)); return res; } void remove_whitespace(std::string& s); bool is_whitespace(std::string_view s); enum SyncCout { IO_LOCK, IO_UNLOCK }; std::ostream& operator<<(std::ostream&, SyncCout); #define sync_cout std::cout << IO_LOCK #define sync_endl std::endl << IO_UNLOCK void sync_cout_start(); void sync_cout_end(); // True if and only if the binary is compiled on a little-endian machine static inline const std::uint16_t Le = 1; static inline const bool IsLittleEndian = *reinterpret_cast(&Le) == 1; template class ValueList { public: std::size_t size() const { return size_; } int ssize() const { return int(size_); } void push_back(const T& value) { assert(size_ < MaxSize); values_[size_++] = value; } const T* begin() const { return values_; } const T* end() const { return values_ + size_; } const T& operator[](int index) const { return values_[index]; } T* make_space(size_t count) { T* result = &values_[size_]; size_ += count; assert(size_ <= MaxSize); return result; } private: T values_[MaxSize]; std::size_t size_ = 0; }; template class MultiArray; namespace Detail { template struct MultiArrayHelper { using ChildType = MultiArray; }; template struct MultiArrayHelper { using ChildType = T; }; template constexpr bool is_strictly_assignable_v = std::is_assignable_v && (std::is_same_v || !std::is_convertible_v); } // MultiArray is a generic N-dimensional array. // The template parameters (Size and Sizes) encode the dimensions of the array. template class MultiArray { using ChildType = typename Detail::MultiArrayHelper::ChildType; using ArrayType = std::array; ArrayType data_; public: using value_type = typename ArrayType::value_type; using size_type = typename ArrayType::size_type; using difference_type = typename ArrayType::difference_type; using reference = typename ArrayType::reference; using const_reference = typename ArrayType::const_reference; using pointer = typename ArrayType::pointer; using const_pointer = typename ArrayType::const_pointer; using iterator = typename ArrayType::iterator; using const_iterator = typename ArrayType::const_iterator; using reverse_iterator = typename ArrayType::reverse_iterator; using const_reverse_iterator = typename ArrayType::const_reverse_iterator; constexpr auto& at(size_type index) noexcept { return data_.at(index); } constexpr const auto& at(size_type index) const noexcept { return data_.at(index); } constexpr auto& operator[](size_type index) noexcept { return data_[index]; } constexpr const auto& operator[](size_type index) const noexcept { return data_[index]; } constexpr auto& front() noexcept { return data_.front(); } constexpr const auto& front() const noexcept { return data_.front(); } constexpr auto& back() noexcept { return data_.back(); } constexpr const auto& back() const noexcept { return data_.back(); } auto* data() { return data_.data(); } const auto* data() const { return data_.data(); } constexpr auto begin() noexcept { return data_.begin(); } constexpr auto end() noexcept { return data_.end(); } constexpr auto begin() const noexcept { return data_.begin(); } constexpr auto end() const noexcept { return data_.end(); } constexpr auto cbegin() const noexcept { return data_.cbegin(); } constexpr auto cend() const noexcept { return data_.cend(); } constexpr auto rbegin() noexcept { return data_.rbegin(); } constexpr auto rend() noexcept { return data_.rend(); } constexpr auto rbegin() const noexcept { return data_.rbegin(); } constexpr auto rend() const noexcept { return data_.rend(); } constexpr auto crbegin() const noexcept { return data_.crbegin(); } constexpr auto crend() const noexcept { return data_.crend(); } constexpr bool empty() const noexcept { return data_.empty(); } constexpr size_type size() const noexcept { return data_.size(); } constexpr size_type max_size() const noexcept { return data_.max_size(); } template void fill(const U& v) { static_assert(Detail::is_strictly_assignable_v, "Cannot assign fill value to entry type"); for (auto& ele : data_) { if constexpr (sizeof...(Sizes) == 0) ele = v; else ele.fill(v); } } constexpr void swap(MultiArray& other) noexcept { data_.swap(other.data_); } }; // xorshift64star Pseudo-Random Number Generator // This class is based on original code written and dedicated // to the public domain by Sebastiano Vigna (2014). // It has the following characteristics: // // - Outputs 64-bit numbers // - Passes Dieharder and SmallCrush test batteries // - Does not require warm-up, no zeroland to escape // - Internal state is a single 64-bit integer // - Period is 2^64 - 1 // - Speed: 1.60 ns/call (Core i7 @3.40GHz) // // For further analysis see // class PRNG { uint64_t s; uint64_t rand64() { s ^= s >> 12, s ^= s << 25, s ^= s >> 27; return s * 2685821657736338717LL; } public: PRNG(uint64_t seed) : s(seed) { assert(seed); } template T rand() { return T(rand64()); } // Special generator used to fast init magic numbers. // Output values only have 1/8th of their bits set on average. template T sparse_rand() { return T(rand64() & rand64() & rand64()); } }; inline uint64_t mul_hi64(uint64_t a, uint64_t b) { #if defined(__GNUC__) && defined(IS_64BIT) __extension__ using uint128 = unsigned __int128; return (uint128(a) * uint128(b)) >> 64; #else uint64_t aL = uint32_t(a), aH = a >> 32; uint64_t bL = uint32_t(b), bH = b >> 32; uint64_t c1 = (aL * bL) >> 32; uint64_t c2 = aH * bL + c1; uint64_t c3 = aL * bH + uint32_t(c2); return aH * bH + (c2 >> 32) + (c3 >> 32); #endif } uint64_t hash_bytes(const char*, size_t); template inline std::size_t get_raw_data_hash(const T& value) { // We must have no padding bytes because we're reinterpreting as char static_assert(std::has_unique_object_representations()); return static_cast( hash_bytes(reinterpret_cast(&value), sizeof(value))); } template inline void hash_combine(std::size_t& seed, const T& v) { std::size_t x; // For primitive types we avoid using the default hasher, which may be // nondeterministic across program invocations if constexpr (std::is_integral()) x = v; else x = std::hash{}(v); seed ^= x + 0x9e3779b9 + (seed << 6) + (seed >> 2); } inline std::uint64_t hash_string(const std::string& sv) { return hash_bytes(sv.data(), sv.size()); } template class FixedString { public: FixedString() : length_(0) { data_[0] = '\0'; } FixedString(const char* str) { size_t len = std::strlen(str); if (len > Capacity) std::terminate(); std::memcpy(data_, str, len); length_ = len; data_[length_] = '\0'; } FixedString(const std::string& str) { if (str.size() > Capacity) std::terminate(); std::memcpy(data_, str.data(), str.size()); length_ = str.size(); data_[length_] = '\0'; } std::size_t size() const { return length_; } std::size_t capacity() const { return Capacity; } const char* c_str() const { return data_; } const char* data() const { return data_; } char& operator[](std::size_t i) { return data_[i]; } const char& operator[](std::size_t i) const { return data_[i]; } FixedString& operator+=(const char* str) { size_t len = std::strlen(str); if (length_ + len > Capacity) std::terminate(); std::memcpy(data_ + length_, str, len); length_ += len; data_[length_] = '\0'; return *this; } FixedString& operator+=(const FixedString& other) { return (*this += other.c_str()); } operator std::string() const { return std::string(data_, length_); } operator std::string_view() const { return std::string_view(data_, length_); } template bool operator==(const T& other) const noexcept { return (std::string_view) (*this) == other; } template bool operator!=(const T& other) const noexcept { return (std::string_view) (*this) != other; } void clear() { length_ = 0; data_[0] = '\0'; } private: char data_[Capacity + 1]; // +1 for null terminator std::size_t length_; }; struct CommandLine { public: CommandLine(int _argc, char** _argv) : argc(_argc), argv(_argv) {} static std::string get_binary_directory(std::string argv0); static std::string get_working_directory(); int argc; char** argv; }; namespace Utility { template void move_to_front(std::vector& vec, Predicate pred) { auto it = std::find_if(vec.begin(), vec.end(), pred); if (it != vec.end()) { std::rotate(vec.begin(), it, it + 1); } } } #if defined(__GNUC__) #define sf_always_inline __attribute__((always_inline)) #elif defined(_MSC_VER) #define sf_always_inline __forceinline #else // do nothing for other compilers #define sf_always_inline #endif #if defined(__clang__) #define sf_assume(cond) __builtin_assume(cond) #elif defined(__GNUC__) #if __GNUC__ >= 13 #define sf_assume(cond) __attribute__((assume(cond))) #else #define sf_assume(cond) \ do \ { \ if (!(cond)) \ __builtin_unreachable(); \ } while (0) #endif #elif defined(_MSC_VER) #define sf_assume(cond) __assume(cond) #else // do nothing for other compilers #define sf_assume(cond) #endif #ifdef __GNUC__ #define sf_unreachable() __builtin_unreachable() #elif defined(_MSC_VER) #define sf_unreachable() __assume(0) #else #define sf_unreachable() #endif } // namespace Stockfish template struct std::hash> { std::size_t operator()(const Stockfish::FixedString& fstr) const noexcept { return Stockfish::hash_bytes(fstr.data(), fstr.size()); } }; #endif // #ifndef MISC_H_INCLUDED