/*
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
#include
#include // IWYU pragma: keep
// IWYU pragma: no_include <__exception/terminate.h>
#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 {
using u64 = std::uint64_t;
using u32 = std::uint32_t;
using u16 = std::uint16_t;
using u8 = std::uint8_t;
using i64 = std::int64_t;
using i32 = std::int32_t;
using i16 = std::int16_t;
using i8 = std::int8_t;
using usize = std::size_t;
using isize = std::ptrdiff_t;
#if defined(__GNUC__) && defined(IS_64BIT)
__extension__ using u128 = unsigned __int128;
__extension__ using i128 = signed __int128;
#endif
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::filesystem::path& fname);
usize str_to_size_t(const std::string& s);
std::string utf8_from_wstring(std::wstring_view s);
std::filesystem::path path_from_utf8(const std::string& path);
#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(i64 value, int slot = 0);
void dbg_stdev_of(i64 value, int slot = 0);
void dbg_extremes_of(i64 value, int slot = 0);
void dbg_correl_of(i64 value1, i64 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(i64), "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;
usize begin = 0;
for (;;)
{
const usize 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 u16 Le = 1;
static inline const bool IsLittleEndian = *reinterpret_cast(&Le) == 1;
template
class ValueList {
public:
usize size() const { return size_; }
int ssize() const { return int(size_); }
void push_back(const T& value) {
assert(size_ < MaxSize);
values_[size_++] = value;
}
// pushes back value if value < max
void push_back_if_lt(const T& value, const T& max) {
assert(size_ < MaxSize);
values_[size_] = value;
size_ += (value < max);
}
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(usize count) {
T* result = &values_[size_];
size_ += count;
assert(size_ <= MaxSize);
return result;
}
private:
T values_[MaxSize];
usize 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) { return data_.at(index); }
constexpr const auto& at(size_type index) const { return data_.at(index); }
constexpr auto& operator[](size_type index) noexcept {
assert(index < Size);
return data_[index];
}
constexpr const auto& operator[](size_type index) const noexcept {
assert(index < Size);
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_); }
};
// Wrapper around std::atomic which uses relaxed accesses or plain
// accesses, depending on the config. Intended use is e.g. wasm where
// the overhead of atomic instructions can be significant, and we only
// require non-tearing for the updates, while ensuring we use relaxed
// accesses otherwise.
template
class RelaxedAtomic {
static constexpr bool UseAtomic =
#ifdef USE_SLOPPY_ATOMICS
!std::atomic::is_always_lock_free || sizeof(T) > sizeof(usize);
#else
true;
#endif
public:
RelaxedAtomic() = default;
RelaxedAtomic(T val) :
inner(val) {}
RelaxedAtomic(const RelaxedAtomic& a) :
inner(static_cast(a)) {}
T operator=(T val) {
if constexpr (UseAtomic)
inner.store(val, std::memory_order_relaxed);
else
inner = val;
return val;
}
RelaxedAtomic& operator=(const RelaxedAtomic& a) {
this->store(static_cast(a), std::memory_order_relaxed);
return *this;
}
operator T() const {
if constexpr (UseAtomic)
return inner.load(std::memory_order_relaxed);
else
return inner;
}
RelaxedAtomic& operator+=(T val) {
T res = this->load(std::memory_order_relaxed) + val;
this->store(res, std::memory_order_relaxed);
return *this;
}
RelaxedAtomic& operator++() {
T res = this->load(std::memory_order_relaxed) + 1;
this->store(res, std::memory_order_relaxed);
return *this;
}
RelaxedAtomic& operator--() {
T res = this->load(std::memory_order_relaxed) - 1;
this->store(res, std::memory_order_relaxed);
return *this;
}
T operator++(int) {
T val = this->load(std::memory_order_relaxed);
this->store(val + 1, std::memory_order_relaxed);
return val;
}
T operator--(int) {
T val = this->load(std::memory_order_relaxed);
this->store(val - 1, std::memory_order_relaxed);
return val;
}
RelaxedAtomic& operator-=(T val) {
T res = this->load(std::memory_order_relaxed) - val;
this->store(res, std::memory_order_relaxed);
return *this;
}
T load(std::memory_order order) const {
assert(order == std::memory_order_relaxed);
if constexpr (UseAtomic)
return inner.load(order);
else
return inner;
}
void store(T val, std::memory_order order) {
assert(order == std::memory_order_relaxed);
if constexpr (UseAtomic)
inner.store(val, order);
else
inner = val;
}
private:
std::conditional_t, T> inner;
};
// 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 {
u64 s;
u64 rand64() {
s ^= s >> 12, s ^= s << 25, s ^= s >> 27;
return s * 2685821657736338717LL;
}
public:
PRNG(u64 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 usize mul_hi64(u64 a, usize b) {
#if defined(__GNUC__) && defined(IS_64BIT) && !defined(__wasm__)
return (u128(a) * u128(b)) >> 64;
#else
u64 aL = u32(a), aH = a >> 32;
u64 bL = u32(b), bH = u64(b) >> 32;
u64 c1 = (aL * bL) >> 32;
u64 c2 = aH * bL + c1;
u64 c3 = aL * bH + u32(c2);
return aH * bH + (c2 >> 32) + (c3 >> 32);
#endif
}
template
inline constexpr T2 interpolate(T1 x, T1 x0, T1 x1, T2 y0, T2 y1) {
assert(x0 != x1);
return T2(y0 + (y1 - y0) * (x - x0) / (x1 - x0));
}
u64 hash_bytes(const char*, usize);
template
inline usize 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(usize& seed, const T& v) {
usize 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 u64 hash_string(const std::string& sv) { return hash_bytes(sv.data(), sv.size()); }
struct CommandLine {
public:
CommandLine(int _argc, char** _argv);
CommandLine(const CommandLine&) = delete;
CommandLine& operator=(const CommandLine&) = delete;
CommandLine(CommandLine&&) = default;
CommandLine& operator=(CommandLine&&) = default;
static std::filesystem::path get_binary_directory(std::filesystem::path argv0);
static std::filesystem::path get_working_directory();
int argc;
char** argv;
private:
#ifdef _WIN32
std::vector argv_storage;
std::vector argv_utf8;
#endif
};
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
#ifdef __GNUC__
#define RESTRICT __restrict__
#elif defined(_MSC_VER)
#define RESTRICT __restrict
#else
#define RESTRICT
#endif
void set_console_utf8();
} // namespace Stockfish
#endif // #ifndef MISC_H_INCLUDED