aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp
diff options
context:
space:
mode:
authorAlexander Smirnov <alex@ydb.tech>2024-11-08 08:53:31 +0000
committerAlexander Smirnov <alex@ydb.tech>2024-11-08 08:53:31 +0000
commit826596ba5418687a87696a06770b2350dec7793d (patch)
treea9111b318a9dd68d20785dc39e45d8b1404c27b4 /library/cpp
parent37b6f076aac14aa8c23251c80178aa3c6bb663b3 (diff)
parent726d467e9898a4afca4523f30dd420cd2a85b43c (diff)
downloadydb-826596ba5418687a87696a06770b2350dec7793d.tar.gz
Merge branch 'rightlib' into mergelibs-241108-0852
Diffstat (limited to 'library/cpp')
-rw-r--r--library/cpp/monlib/service/pages/mon_page.h1
-rw-r--r--library/cpp/threading/thread_local/thread_local.h6
-rw-r--r--library/cpp/tld/tlds-alpha-by-domain.txt2
-rw-r--r--library/cpp/yt/memory/atomic_intrusive_ptr-inl.h6
-rw-r--r--library/cpp/yt/memory/atomic_intrusive_ptr.h6
-rw-r--r--library/cpp/yt/memory/chunked_memory_pool-inl.h7
-rw-r--r--library/cpp/yt/memory/intrusive_ptr.h9
-rw-r--r--library/cpp/yt/memory/new-inl.h12
-rw-r--r--library/cpp/yt/memory/new.cpp18
-rw-r--r--library/cpp/yt/memory/weak_ptr.h1
-rw-r--r--library/cpp/yt/memory/ya.make1
-rw-r--r--library/cpp/yt/misc/arcadia_enum-inl.h12
-rw-r--r--library/cpp/yt/misc/cast-inl.h55
-rw-r--r--library/cpp/yt/misc/cast.h10
-rw-r--r--library/cpp/yt/misc/compare-inl.h71
-rw-r--r--library/cpp/yt/misc/compare.h28
-rw-r--r--library/cpp/yt/misc/enum-inl.h10
-rw-r--r--library/cpp/yt/misc/enum.h2
-rw-r--r--library/cpp/yt/misc/hash-inl.h15
-rw-r--r--library/cpp/yt/misc/hash.h9
-rw-r--r--library/cpp/yt/misc/numeric_helpers-inl.h59
-rw-r--r--library/cpp/yt/misc/numeric_helpers.h31
-rw-r--r--library/cpp/yt/misc/unittests/cast_ut.cpp45
-rw-r--r--library/cpp/yt/misc/unittests/compare_ut.cpp62
-rw-r--r--library/cpp/yt/misc/unittests/enum_ut.cpp38
-rw-r--r--library/cpp/yt/misc/unittests/hash_ut.cpp19
-rw-r--r--library/cpp/yt/misc/unittests/ya.make3
-rw-r--r--library/cpp/yt/stockpile/stockpile_linux.cpp6
-rw-r--r--library/cpp/yt/system/exit-inl.h32
-rw-r--r--library/cpp/yt/system/exit.cpp16
-rw-r--r--library/cpp/yt/system/exit.h30
-rw-r--r--library/cpp/yt/system/ya.make4
32 files changed, 554 insertions, 72 deletions
diff --git a/library/cpp/monlib/service/pages/mon_page.h b/library/cpp/monlib/service/pages/mon_page.h
index a10d0cdc7e5..dcc021c0584 100644
--- a/library/cpp/monlib/service/pages/mon_page.h
+++ b/library/cpp/monlib/service/pages/mon_page.h
@@ -11,6 +11,7 @@ namespace NMonitoring {
static const char HTTPOKBIN[] = "HTTP/1.1 200 Ok\r\nContent-Type: application/octet-stream\r\nConnection: Close\r\n\r\n";
static const char HTTPOKHTML[] = "HTTP/1.1 200 Ok\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n";
static const char HTTPOKJSON[] = "HTTP/1.1 200 Ok\r\nContent-Type: application/json\r\nConnection: Close\r\n\r\n";
+ static const char HTTPOKYAML[] = "HTTP/1.1 200 Ok\r\nContent-Type: application/yaml\r\nConnection: Close\r\n\r\n";
static const char HTTPOKSPACK[] = "HTTP/1.1 200 Ok\r\nContent-Type: application/x-solomon-spack\r\nConnection: Close\r\n\r\n";
static const char HTTPOKPROMETHEUS[] = "HTTP/1.1 200 Ok\r\nContent-Type: text/plain\r\nConnection: Close\r\n\r\n";
static const char HTTPOKUNISTAT[] = "HTTP/1.1 200 Ok\r\nContent-Type: text/json\r\nConnection: Close\r\n\r\n";
diff --git a/library/cpp/threading/thread_local/thread_local.h b/library/cpp/threading/thread_local/thread_local.h
index bb0d11347db..15cb43d37f3 100644
--- a/library/cpp/threading/thread_local/thread_local.h
+++ b/library/cpp/threading/thread_local/thread_local.h
@@ -220,8 +220,8 @@ public:
, Pool_{0}
{}
- template <typename ...ConsturctArgs>
- T* Get(TThread::TId tid, ConsturctArgs&& ...args) {
+ template <typename ...ConstructArgs>
+ T* Get(TThread::TId tid, ConstructArgs&& ...args) {
TNode* head = Head_.load(std::memory_order_acquire);
for (TNode* node = head; node; node = node->Next) {
if (node->Key == tid) {
@@ -229,7 +229,7 @@ public:
}
}
- TNode* newNode = AllocateNode(tid, head, std::forward<ConsturctArgs>(args)...);
+ TNode* newNode = AllocateNode(tid, head, std::forward<ConstructArgs>(args)...);
while (!Head_.compare_exchange_weak(head, newNode, std::memory_order_release, std::memory_order_relaxed)) {
newNode->Next = head;
}
diff --git a/library/cpp/tld/tlds-alpha-by-domain.txt b/library/cpp/tld/tlds-alpha-by-domain.txt
index cd5a49783fd..2a7c080416b 100644
--- a/library/cpp/tld/tlds-alpha-by-domain.txt
+++ b/library/cpp/tld/tlds-alpha-by-domain.txt
@@ -1,4 +1,4 @@
-# Version 2024110100, Last Updated Fri Nov 1 07:07:01 2024 UTC
+# Version 2024110700, Last Updated Thu Nov 7 07:07:01 2024 UTC
AAA
AARP
ABB
diff --git a/library/cpp/yt/memory/atomic_intrusive_ptr-inl.h b/library/cpp/yt/memory/atomic_intrusive_ptr-inl.h
index e337a78bcfc..ffb2aaff5f3 100644
--- a/library/cpp/yt/memory/atomic_intrusive_ptr-inl.h
+++ b/library/cpp/yt/memory/atomic_intrusive_ptr-inl.h
@@ -237,6 +237,12 @@ typename TAtomicIntrusivePtr<T>::TRawPtr TAtomicIntrusivePtr<T>::Get() const
}
template <class T>
+typename TAtomicIntrusivePtr<T>::TRawPtr TAtomicIntrusivePtr<T>::get() const
+{
+ return Get();
+}
+
+template <class T>
TPackedPtr TAtomicIntrusivePtr<T>::AcquireObject(T* obj, bool consumeRef)
{
if (obj) {
diff --git a/library/cpp/yt/memory/atomic_intrusive_ptr.h b/library/cpp/yt/memory/atomic_intrusive_ptr.h
index 4bab78ef580..243920bdabd 100644
--- a/library/cpp/yt/memory/atomic_intrusive_ptr.h
+++ b/library/cpp/yt/memory/atomic_intrusive_ptr.h
@@ -20,6 +20,9 @@ template <class T>
class TAtomicIntrusivePtr
{
public:
+ using TUnderlying = T;
+ using element_type = T;
+
TAtomicIntrusivePtr() = default;
TAtomicIntrusivePtr(std::nullptr_t);
@@ -45,6 +48,9 @@ public:
//! Result is only suitable for comparison, not dereference.
TRawPtr Get() const;
+ //! Result is only suitable for comparison, not dereference.
+ TRawPtr get() const;
+
explicit operator bool() const;
private:
diff --git a/library/cpp/yt/memory/chunked_memory_pool-inl.h b/library/cpp/yt/memory/chunked_memory_pool-inl.h
index 25022041607..00733235f49 100644
--- a/library/cpp/yt/memory/chunked_memory_pool-inl.h
+++ b/library/cpp/yt/memory/chunked_memory_pool-inl.h
@@ -31,9 +31,10 @@ TDerived* TAllocationHolder::Allocate(size_t size, TRefCountedTypeCookie cookie)
{
auto requestedSize = sizeof(TDerived) + size;
auto* ptr = ::malloc(requestedSize);
-
- if (!ptr) {
- AbortProcess(ToUnderlying(EProcessExitCode::OutOfMemory));
+ if (Y_UNLIKELY(!ptr)) {
+ AbortProcessDramatically(
+ EProcessExitCode::OutOfMemory,
+ "Out-of-memory during chunked memory pool allocation");
}
#ifndef _win_
diff --git a/library/cpp/yt/memory/intrusive_ptr.h b/library/cpp/yt/memory/intrusive_ptr.h
index 91b15279aec..1a168cec0ec 100644
--- a/library/cpp/yt/memory/intrusive_ptr.h
+++ b/library/cpp/yt/memory/intrusive_ptr.h
@@ -18,6 +18,9 @@ class TIntrusivePtr
public:
using TUnderlying = T;
+ //! For compatibility with std:: smart pointers.
+ using element_type = T;
+
constexpr TIntrusivePtr() noexcept
{ }
@@ -148,6 +151,12 @@ public:
return T_;
}
+ //! Returns the pointer, for compatibility with std:: smart pointers.
+ T* get() const noexcept
+ {
+ return T_;
+ }
+
//! Returns the pointer and releases the ownership.
T* Release() noexcept
{
diff --git a/library/cpp/yt/memory/new-inl.h b/library/cpp/yt/memory/new-inl.h
index f0646582ec8..53308fb254e 100644
--- a/library/cpp/yt/memory/new-inl.h
+++ b/library/cpp/yt/memory/new-inl.h
@@ -10,8 +10,6 @@
#include <library/cpp/yt/malloc//malloc.h>
-#include <library/cpp/yt/system/exit.h>
-
namespace NYT {
////////////////////////////////////////////////////////////////////////////////
@@ -178,6 +176,8 @@ Y_FORCE_INLINE TIntrusivePtr<T> SafeConstruct(void* ptr, As&&... args)
}
}
+void AbortOnOom();
+
template <size_t Size, size_t Alignment>
Y_FORCE_INLINE void* AllocateConstSizeAlignedOrCrash()
{
@@ -192,7 +192,7 @@ Y_FORCE_INLINE void* AllocateConstSizeAlignedOrCrash()
}
#endif
if (Y_UNLIKELY(!ptr)) {
- AbortProcess(ToUnderlying(EProcessExitCode::OutOfMemory));
+ AbortOnOom();
}
return ptr;
}
@@ -211,7 +211,7 @@ Y_FORCE_INLINE void* AllocateOrCrash(size_t size)
}
#endif
if (Y_UNLIKELY(!ptr)) {
- AbortProcess(ToUnderlying(EProcessExitCode::OutOfMemory));
+ AbortOnOom();
}
return ptr;
}
@@ -249,7 +249,7 @@ Y_FORCE_INLINE TIntrusivePtr<T> New(
{
auto obj = TryNew<T>(allocator, std::forward<As>(args)...);
if (Y_UNLIKELY(!obj)) {
- AbortProcess(ToUnderlying(EProcessExitCode::OutOfMemory));
+ NYT::NDetail::AbortOnOom();
}
return obj;
}
@@ -288,7 +288,7 @@ Y_FORCE_INLINE TIntrusivePtr<T> NewWithExtraSpace(
{
auto obj = TryNewWithExtraSpace<T>(allocator, extraSpaceSize, std::forward<As>(args)...);
if (Y_UNLIKELY(!obj)) {
- AbortProcess(ToUnderlying(EProcessExitCode::OutOfMemory));
+ NYT::NDetail::AbortOnOom();
}
return obj;
}
diff --git a/library/cpp/yt/memory/new.cpp b/library/cpp/yt/memory/new.cpp
new file mode 100644
index 00000000000..0b4a4b3b8b8
--- /dev/null
+++ b/library/cpp/yt/memory/new.cpp
@@ -0,0 +1,18 @@
+#include "new.h"
+
+#include <library/cpp/yt/system/exit.h>
+
+namespace NYT::NDetail {
+
+////////////////////////////////////////////////////////////////////////////////
+
+void AbortOnOom()
+{
+ AbortProcessDramatically(
+ EProcessExitCode::OutOfMemory,
+ "Out-of-memory during object allocation");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT::NDetail
diff --git a/library/cpp/yt/memory/weak_ptr.h b/library/cpp/yt/memory/weak_ptr.h
index c647198aaf5..e7b847653d4 100644
--- a/library/cpp/yt/memory/weak_ptr.h
+++ b/library/cpp/yt/memory/weak_ptr.h
@@ -13,6 +13,7 @@ class TWeakPtr
{
public:
using TUnderlying = T;
+ using element_type = T;
//! Empty constructor.
TWeakPtr() = default;
diff --git a/library/cpp/yt/memory/ya.make b/library/cpp/yt/memory/ya.make
index bd817454a93..5397dccf32f 100644
--- a/library/cpp/yt/memory/ya.make
+++ b/library/cpp/yt/memory/ya.make
@@ -15,6 +15,7 @@ SRCS(
chunked_memory_pool_output.cpp
chunked_output_stream.cpp
memory_tag.cpp
+ new.cpp
ref.cpp
ref_tracked.cpp
safe_memory_reader.cpp
diff --git a/library/cpp/yt/misc/arcadia_enum-inl.h b/library/cpp/yt/misc/arcadia_enum-inl.h
index 17a10bb3b22..1df8b2c4647 100644
--- a/library/cpp/yt/misc/arcadia_enum-inl.h
+++ b/library/cpp/yt/misc/arcadia_enum-inl.h
@@ -42,6 +42,18 @@ struct TArcadiaEnumTraitsImpl
auto it = LiteralToValue.find(literal);
return it == LiteralToValue.end() ? std::nullopt : std::make_optional(it->second);
}
+
+ static bool IsKnownValue(T value)
+ {
+ static const auto Values = [] {
+ THashSet<T> result;
+ for (const auto& [value, _] : GetEnumNames<T>()) {
+ result.insert(value);
+ }
+ return result;
+ }();
+ return Values.contains(value);
+ }
};
////////////////////////////////////////////////////////////////////////////////
diff --git a/library/cpp/yt/misc/cast-inl.h b/library/cpp/yt/misc/cast-inl.h
index 00e6e240b82..50920f01934 100644
--- a/library/cpp/yt/misc/cast-inl.h
+++ b/library/cpp/yt/misc/cast-inl.h
@@ -94,20 +94,19 @@ constexpr bool IsInIntegralRange(S value)
}
template <class T, class S>
-constexpr bool TryIntegralCast(S value, T* result)
+constexpr std::optional<T> TryCheckedIntegralCast(S value)
{
- if (!NDetail::IsInIntegralRange<T>(value)) {
- return false;
+ [[unlikely]] if (!NDetail::IsInIntegralRange<T>(value)) {
+ return std::nullopt;
}
- *result = static_cast<T>(value);
- return true;
+ return static_cast<T>(value);
}
template <class T, class S>
T CheckedIntegralCast(S value)
{
- T result;
- if (!TryIntegralCast<T>(value, &result)) {
+ auto result = TryCheckedIntegralCast<T>(value);
+ if (!result) {
throw TSimpleException(Sprintf("Error casting %s value \"%s\" to %s: value is out of expected range [%s; %s]",
TypeName<S>().c_str(),
NYT::NDetail::FormatInvalidCastValue(value).c_str(),
@@ -115,35 +114,43 @@ T CheckedIntegralCast(S value)
::ToString(std::numeric_limits<T>::lowest()).c_str(),
::ToString(std::numeric_limits<T>::max()).c_str()));
}
- return result;
+ return *result;
}
template <class T, class S>
-constexpr bool TryEnumCast(S value, T* result)
+ requires TEnumTraits<T>::IsEnum
+constexpr std::optional<T> TryCheckedEnumCast(S value)
{
- std::underlying_type_t<T> underlying;
- if (!TryIntegralCast<std::underlying_type_t<T>>(value, &underlying)) {
- return false;
+ auto underlying = TryCheckedIntegralCast<std::underlying_type_t<T>>(value);
+ [[unlikely]] if (!underlying) {
+ return std::nullopt;
}
- auto candidate = static_cast<T>(underlying);
- if (!TEnumTraits<T>::FindLiteralByValue(candidate)) {
- return false;
+ auto candidate = static_cast<T>(*underlying);
+ [[unlikely]] if (!TEnumTraits<T>::IsValidValue(candidate)) {
+ return std::nullopt;
}
- *result = candidate;
- return true;
+ return candidate;
}
template <class T, class S>
+ requires TEnumTraits<T>::IsEnum
T CheckedEnumCast(S value)
{
- T result;
- if (!TryEnumCast<T>(value, &result)) {
- throw TSimpleException(Sprintf("Error casting %s value \"%d\" to enum %s",
- TypeName<S>().c_str(),
- static_cast<int>(value),
- TEnumTraits<T>::GetTypeName().data()));
+ auto result = TryCheckedEnumCast<T>(value);
+ [[unlikely]] if (!result) {
+ if constexpr (std::is_signed_v<S>) {
+ throw TSimpleException(Sprintf("Error casting %s value \"%" PRIi64 "\" to enum %s",
+ TypeName<S>().c_str(),
+ static_cast<i64>(value),
+ TEnumTraits<T>::GetTypeName().data()));
+ } else {
+ throw TSimpleException(Sprintf("Error casting %s value \"%" PRIu64 "\" to enum %s",
+ TypeName<S>().c_str(),
+ static_cast<ui64>(value),
+ TEnumTraits<T>::GetTypeName().data()));
+ }
}
- return result;
+ return *result;
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/library/cpp/yt/misc/cast.h b/library/cpp/yt/misc/cast.h
index e8654cc1222..a4e32e0287d 100644
--- a/library/cpp/yt/misc/cast.h
+++ b/library/cpp/yt/misc/cast.h
@@ -1,7 +1,11 @@
#pragma once
+#include "enum.h"
+
#include <library/cpp/yt/exception/exception.h>
+#include <optional>
+
namespace NYT {
////////////////////////////////////////////////////////////////////////////////
@@ -13,7 +17,7 @@ template <class T, class S>
constexpr bool IsInIntegralRange(S value);
template <class T, class S>
-constexpr bool TryIntegralCast(S value, T* result);
+constexpr std::optional<T> TryCheckedIntegralCast(S value);
template <class T, class S>
T CheckedIntegralCast(S value);
@@ -21,9 +25,11 @@ T CheckedIntegralCast(S value);
////////////////////////////////////////////////////////////////////////////////
template <class T, class S>
-constexpr bool TryEnumCast(S value, T* result);
+ requires TEnumTraits<T>::IsEnum
+constexpr std::optional<T> TryCheckedEnumCast(S value);
template <class T, class S>
+ requires TEnumTraits<T>::IsEnum
T CheckedEnumCast(S value);
////////////////////////////////////////////////////////////////////////////////
diff --git a/library/cpp/yt/misc/compare-inl.h b/library/cpp/yt/misc/compare-inl.h
new file mode 100644
index 00000000000..b9dfb5319f5
--- /dev/null
+++ b/library/cpp/yt/misc/compare-inl.h
@@ -0,0 +1,71 @@
+#ifndef COMPARE_INL_H_
+#error "Direct inclusion of this file is not allowed, include compare.h"
+// For the sake of sane code completion.
+#include "compare.h"
+#endif
+
+#include "numeric_helpers.h"
+
+#include <util/generic/string.h>
+
+#include <string>
+#include <string_view>
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T>
+Y_FORCE_INLINE int TernaryCompare(const T& lhs, const T& rhs)
+{
+ if (lhs == rhs) {
+ return 0;
+ } else if (lhs < rhs) {
+ return -1;
+ } else {
+ return +1;
+ }
+}
+
+//! An optimized version for string types.
+template <class T>
+ requires
+ std::is_same_v<T, TString> ||
+ std::is_same_v<T, TStringBuf> ||
+ std::is_same_v<T, std::string> ||
+ std::is_same_v<T, std::string_view>
+Y_FORCE_INLINE int TernaryCompare(const T& lhs, const T& rhs)
+{
+ return GetSign(std::string_view(lhs).compare(std::string_view(rhs)));
+}
+
+template <class T>
+Y_FORCE_INLINE int NaNSafeTernaryCompare(const T& lhs, const T& rhs)
+{
+ return TernaryCompare(lhs, rhs);
+}
+
+template <class T>
+ requires std::is_floating_point_v<T>
+Y_FORCE_INLINE int NaNSafeTernaryCompare(const T& lhs, const T& rhs)
+{
+ if (lhs < rhs) {
+ return -1;
+ } else if (lhs > rhs) {
+ return +1;
+ } else if (std::isnan(lhs)) {
+ if (std::isnan(rhs)) {
+ return 0;
+ } else {
+ return +1;
+ }
+ } else if (std::isnan(rhs)) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
diff --git a/library/cpp/yt/misc/compare.h b/library/cpp/yt/misc/compare.h
new file mode 100644
index 00000000000..22e452b355c
--- /dev/null
+++ b/library/cpp/yt/misc/compare.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <util/generic/string.h>
+
+#include <string_view>
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+//! Compares #lhs with #rhs;
+//! returns -1 (if |lhs < rhs|), 0 (if |lhs == rhs|), or 1 (|lhs > rhs|).
+template <class T>
+int TernaryCompare(const T& lhs, const T& rhs);
+
+//! Same as #TernaryCompare but handles NaN values gracefully
+//! (assuming NaNs are larger than any regular number and all NaNs are equal).
+//! If |T| is not a floating-point type, #NaNSafeTernaryCompare is equivalent to #TernaryCompare.
+template <class T>
+int NaNSafeTernaryCompare(const T& lhs, const T& rhs);
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
+
+#define COMPARE_INL_H_
+#include "compare-inl.h"
+#undef COMPARE_INL_H_
diff --git a/library/cpp/yt/misc/enum-inl.h b/library/cpp/yt/misc/enum-inl.h
index f1fb9f63e10..d8037bbf749 100644
--- a/library/cpp/yt/misc/enum-inl.h
+++ b/library/cpp/yt/misc/enum-inl.h
@@ -324,6 +324,16 @@ constexpr bool TEnumTraits<T, true>::IsKnownValue(T value)
}
template <class T>
+constexpr bool TEnumTraits<T, true>::IsValidValue(T value)
+{
+ if constexpr (IsBitEnum) {
+ return (value & TEnumTraits<T>::GetAllSetValue()) == value;
+ } else {
+ return IsKnownValue(value);
+ }
+}
+
+template <class T>
TString TEnumTraits<T, true>::ToString(T value)
{
using ::ToString;
diff --git a/library/cpp/yt/misc/enum.h b/library/cpp/yt/misc/enum.h
index d9611b2823b..331eb59f1a4 100644
--- a/library/cpp/yt/misc/enum.h
+++ b/library/cpp/yt/misc/enum.h
@@ -89,8 +89,10 @@ struct TEnumTraits<T, true>
static constexpr std::optional<T> TryGetUnknownValue();
static std::optional<TStringBuf> FindLiteralByValue(T value);
static std::optional<T> FindValueByLiteral(TStringBuf literal);
+
static constexpr bool IsKnownValue(T value)
requires (!TEnumTraitsImpl<T>::IsBitEnum);
+ static constexpr bool IsValidValue(T value);
static TString ToString(T value);
static T FromString(TStringBuf literal);
diff --git a/library/cpp/yt/misc/hash-inl.h b/library/cpp/yt/misc/hash-inl.h
index 46eeefe6204..159748a09bc 100644
--- a/library/cpp/yt/misc/hash-inl.h
+++ b/library/cpp/yt/misc/hash-inl.h
@@ -4,6 +4,8 @@
#include "hash.h"
#endif
+#include <cmath>
+
namespace NYT {
////////////////////////////////////////////////////////////////////////////////
@@ -29,6 +31,19 @@ void HashCombine(size_t& h, const T& k)
HashCombine(h, THash<T>()(k));
}
+template <class T>
+Y_FORCE_INLINE size_t NaNSafeHash(const T& value)
+{
+ return ::THash<T>()(value);
+}
+
+template <class T>
+ requires std::is_floating_point_v<T>
+Y_FORCE_INLINE size_t NaNSafeHash(const T& value)
+{
+ return std::isnan(value) ? 0 : ::THash<T>()(value);
+}
+
////////////////////////////////////////////////////////////////////////////////
template <class TElement, class TUnderlying>
diff --git a/library/cpp/yt/misc/hash.h b/library/cpp/yt/misc/hash.h
index 2fecf895061..fe4087c3315 100644
--- a/library/cpp/yt/misc/hash.h
+++ b/library/cpp/yt/misc/hash.h
@@ -17,6 +17,12 @@ void HashCombine(size_t& h, size_t k);
template <class T>
void HashCombine(size_t& h, const T& k);
+//! Computes the hash of #value handling NaN values gracefully
+//! (returning the same constant for all NaNs).
+//! If |T| is not a floating-point type, #NaNSafeHash is equivalent to #THash.
+template <class T>
+size_t NaNSafeHash(const T& value);
+
////////////////////////////////////////////////////////////////////////////////
//! Provides a hasher that randomizes the results of another one.
@@ -25,12 +31,11 @@ class TRandomizedHash
{
public:
TRandomizedHash();
- size_t operator () (const TElement& element) const;
+ size_t operator()(const TElement& element) const;
private:
size_t Seed_;
TUnderlying Underlying_;
-
};
////////////////////////////////////////////////////////////////////////////////
diff --git a/library/cpp/yt/misc/numeric_helpers-inl.h b/library/cpp/yt/misc/numeric_helpers-inl.h
new file mode 100644
index 00000000000..c0dbc07490d
--- /dev/null
+++ b/library/cpp/yt/misc/numeric_helpers-inl.h
@@ -0,0 +1,59 @@
+#ifndef NUMERIC_HELPERS_INL_H_
+#error "Direct inclusion of this file is not allowed, include numeric_helpers.h"
+// For the sake of sane code completion.
+#include "numeric_helpers.h"
+#endif
+
+#include <cstdlib>
+#include <cinttypes>
+#include <cmath>
+#include <algorithm>
+
+#include <util/system/compiler.h>
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T>
+T DivCeil(const T& numerator, const T& denominator)
+{
+ YT_VERIFY(denominator != 0);
+ auto res = std::div(numerator, denominator);
+ return res.quot + (res.rem > static_cast<T>(0) ? static_cast<T>(1) : static_cast<T>(0));
+}
+
+template <typename T>
+T DivRound(const T& numerator, const T& denominator)
+{
+ auto res = std::div(numerator, denominator);
+ return res.quot + (res.rem >= (denominator + 1) / 2 ? static_cast<T>(1) : static_cast<T>(0));
+}
+
+template <class T>
+T RoundUp(const T& numerator, const T& denominator)
+{
+ return DivCeil(numerator, denominator) * denominator;
+}
+
+template <class T>
+T RoundDown(const T& numerator, const T& denominator)
+{
+ return (numerator / denominator) * denominator;
+}
+
+template <class T>
+Y_FORCE_INLINE int GetSign(const T& value)
+{
+ if (value < 0) {
+ return -1;
+ } else if (value > 0) {
+ return +1;
+ } else {
+ return 0;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
diff --git a/library/cpp/yt/misc/numeric_helpers.h b/library/cpp/yt/misc/numeric_helpers.h
new file mode 100644
index 00000000000..20844029c27
--- /dev/null
+++ b/library/cpp/yt/misc/numeric_helpers.h
@@ -0,0 +1,31 @@
+#pragma once
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T>
+T DivCeil(const T& numerator, const T& denominator);
+
+//! A version of division that is a bit less noisy around the situation
+//! when numerator is almost divisible by denominator. Round up if the remainder
+//! is at least half of denominator, otherwise round down.
+template<class T>
+T DivRound(const T& numerator, const T& denominator);
+
+template <class T>
+T RoundUp(const T& numerator, const T& denominator);
+
+template <class T>
+T RoundDown(const T& numerator, const T& denominator);
+
+template <class T>
+int GetSign(const T& value);
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
+
+#define NUMERIC_HELPERS_INL_H_
+#include "numeric_helpers-inl.h"
+#undef NUMERIC_HELPERS_INL_H_
diff --git a/library/cpp/yt/misc/unittests/cast_ut.cpp b/library/cpp/yt/misc/unittests/cast_ut.cpp
new file mode 100644
index 00000000000..8662575d6c0
--- /dev/null
+++ b/library/cpp/yt/misc/unittests/cast_ut.cpp
@@ -0,0 +1,45 @@
+#include <library/cpp/testing/gtest/gtest.h>
+
+#include <library/cpp/yt/misc/cast.h>
+#include <library/cpp/yt/misc/enum.h>
+
+namespace NYT {
+namespace {
+
+////////////////////////////////////////////////////////////////////////////////
+
+DEFINE_ENUM_WITH_UNDERLYING_TYPE(ECardinal, char,
+ ((West) (0))
+ ((North) (1))
+ ((East) (2))
+ ((South) (3))
+);
+
+DEFINE_BIT_ENUM_WITH_UNDERLYING_TYPE(EFeatures, ui8,
+ ((None) (0x0000))
+ ((First) (0x0001))
+ ((Second)(0x0002))
+);
+
+TEST(TCastTest, TryCheckedEnumCast)
+{
+ EXPECT_EQ((TryCheckedEnumCast<ECardinal, char>(2)), ECardinal::East);
+ EXPECT_EQ((TryCheckedEnumCast<ECardinal, int>(3)), ECardinal::South);
+
+ EXPECT_FALSE((TryCheckedEnumCast<ECardinal, char>(100)));
+ EXPECT_FALSE((TryCheckedEnumCast<ECardinal, int>(300)));
+
+ EXPECT_EQ((TryCheckedEnumCast<EFeatures, ui8>(0)), EFeatures::None);
+ EXPECT_EQ((TryCheckedEnumCast<EFeatures, ui8>(ToUnderlying(EFeatures::First))), EFeatures::First);
+ EXPECT_EQ((TryCheckedEnumCast<EFeatures, ui8>(ToUnderlying(EFeatures::Second))), EFeatures::Second);
+ EXPECT_EQ((TryCheckedEnumCast<EFeatures, int>(ToUnderlying(EFeatures::First))), EFeatures::First);
+ EXPECT_EQ((TryCheckedEnumCast<EFeatures, ui8>(ToUnderlying(EFeatures::First | EFeatures::Second))), EFeatures::First | EFeatures::Second);
+
+ EXPECT_FALSE((TryCheckedEnumCast<EFeatures, ui8>(0x10)));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace
+} // namespace NYT
+
diff --git a/library/cpp/yt/misc/unittests/compare_ut.cpp b/library/cpp/yt/misc/unittests/compare_ut.cpp
new file mode 100644
index 00000000000..da149a0690e
--- /dev/null
+++ b/library/cpp/yt/misc/unittests/compare_ut.cpp
@@ -0,0 +1,62 @@
+#include <library/cpp/testing/gtest/gtest.h>
+
+#include <library/cpp/yt/misc/compare.h>
+
+namespace NYT {
+namespace {
+
+////////////////////////////////////////////////////////////////////////////////
+
+TEST(TCompareTest, TernaryCompare)
+{
+ EXPECT_EQ(TernaryCompare(123, 123), 0);
+ EXPECT_EQ(TernaryCompare(10, 20), -1);
+ EXPECT_EQ(TernaryCompare(20, 10), +1);
+ EXPECT_EQ(TernaryCompare(std::nan("1"), std::nan("1")), +1);
+ EXPECT_EQ(TernaryCompare(std::nan("1"), std::nan("2")), +1);
+ EXPECT_EQ(TernaryCompare(std::nan("1"), 123.0), +1);
+ EXPECT_EQ(TernaryCompare(123.0, std::nan("1")), +1);
+}
+
+TEST(TCompareTest, NaNSafeTernaryCompare)
+{
+ EXPECT_EQ(NaNSafeTernaryCompare(std::nan("1"), std::nan("1")), 0);
+ EXPECT_EQ(NaNSafeTernaryCompare(std::nan("1"), std::nan("2")), 0);
+ EXPECT_EQ(NaNSafeTernaryCompare(123.0, std::nan("1")), -1);
+ EXPECT_EQ(NaNSafeTernaryCompare(std::nan("1"), 123.0), +1);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T>
+class TTernaryCompareStringTest
+ : public ::testing::Test
+{ };
+
+TYPED_TEST_SUITE_P(TTernaryCompareStringTest);
+
+TYPED_TEST_P(TTernaryCompareStringTest, Compare)
+{
+ EXPECT_EQ(TernaryCompare(TypeParam("abc"), TypeParam("abc")), 0);
+ EXPECT_EQ(TernaryCompare(TypeParam("x"), TypeParam("y")), -1);
+ EXPECT_EQ(TernaryCompare(TypeParam("y"), TypeParam("x")), +1);
+}
+
+REGISTER_TYPED_TEST_SUITE_P(TTernaryCompareStringTest, Compare);
+
+using TTernaryCompareStringTestTypes = ::testing::Types<
+ TString,
+ TStringBuf,
+ std::string,
+ std::string_view
+>;
+
+INSTANTIATE_TYPED_TEST_SUITE_P(
+ TypeParametrized,
+ TTernaryCompareStringTest,
+ TTernaryCompareStringTestTypes);
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace
+} // namespace NYT
diff --git a/library/cpp/yt/misc/unittests/enum_ut.cpp b/library/cpp/yt/misc/unittests/enum_ut.cpp
index fc7feed2279..22bfe1fdc91 100644
--- a/library/cpp/yt/misc/unittests/enum_ut.cpp
+++ b/library/cpp/yt/misc/unittests/enum_ut.cpp
@@ -215,10 +215,21 @@ TEST(TEnumTest, IsKnownValue)
EXPECT_TRUE(TEnumTraits<ESimple>::IsKnownValue(ESimple::X));
EXPECT_TRUE(TEnumTraits<ESimple>::IsKnownValue(ESimple::Y));
EXPECT_TRUE(TEnumTraits<ESimple>::IsKnownValue(ESimple::Z));
-
EXPECT_FALSE(TEnumTraits<ESimple>::IsKnownValue(static_cast<ESimple>(100)));
+}
- EXPECT_TRUE(TEnumTraits<EColor>::IsKnownValue(EColor::Red));
+TEST(TEnumTest, IsValidValue)
+{
+ EXPECT_TRUE(TEnumTraits<ESimple>::IsValidValue(ESimple::X));
+ EXPECT_TRUE(TEnumTraits<ESimple>::IsValidValue(ESimple::Y));
+ EXPECT_TRUE(TEnumTraits<ESimple>::IsValidValue(ESimple::Z));
+ EXPECT_FALSE(TEnumTraits<ESimple>::IsValidValue(static_cast<ESimple>(100)));
+
+ EXPECT_TRUE(TEnumTraits<EFlag>::IsValidValue(EFlag()));
+ EXPECT_TRUE(TEnumTraits<EFlag>::IsValidValue(EFlag::_1));
+ EXPECT_TRUE(TEnumTraits<EFlag>::IsValidValue(EFlag::_1 | EFlag::_2));
+ EXPECT_TRUE(TEnumTraits<EFlag>::IsValidValue(EFlag::_1 | EFlag::_2 | EFlag::_3 | EFlag::_4));
+ EXPECT_FALSE(TEnumTraits<EFlag>::IsValidValue(static_cast<EFlag>(0x10)));
}
TEST(TEnumTest, AllSetValue)
@@ -280,29 +291,6 @@ TEST(TEnumTest, CustomString)
EXPECT_EQ("1_b", ToString(ECustomString::B));
}
-TEST(TEnumTest, Cast)
-{
- ECardinal cardinal;
- {
- char validValue = 2;
- EXPECT_TRUE(TryEnumCast(validValue, &cardinal));
- EXPECT_EQ(cardinal, ECardinal::East);
- }
- {
- char invalidValue = 100;
- EXPECT_FALSE(TryEnumCast(invalidValue, &cardinal));
- }
- {
- int widerTypeValidValue = 3;
- EXPECT_TRUE(TryEnumCast(widerTypeValidValue, &cardinal));
- EXPECT_EQ(cardinal, ECardinal::South);
- }
- {
- int widerTypeInvalueValue = (1 << 8) + 100;
- EXPECT_FALSE(TryEnumCast(widerTypeInvalueValue, &cardinal));
- }
-}
-
TEST(TEnumTest, UnknownValue)
{
EXPECT_EQ(TEnumTraits<EColor>::TryGetUnknownValue(), std::nullopt);
diff --git a/library/cpp/yt/misc/unittests/hash_ut.cpp b/library/cpp/yt/misc/unittests/hash_ut.cpp
new file mode 100644
index 00000000000..0c336be5dc3
--- /dev/null
+++ b/library/cpp/yt/misc/unittests/hash_ut.cpp
@@ -0,0 +1,19 @@
+#include <library/cpp/testing/gtest/gtest.h>
+
+#include <library/cpp/yt/misc/hash.h>
+
+namespace NYT {
+namespace {
+
+////////////////////////////////////////////////////////////////////////////////
+
+TEST(THashTest, NaNSafeHash)
+{
+ EXPECT_EQ(NaNSafeHash(123), THash<int>()(123));
+ EXPECT_EQ(NaNSafeHash(std::nan("1")), NaNSafeHash(std::nan("2")));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace
+} // namespace NYT
diff --git a/library/cpp/yt/misc/unittests/ya.make b/library/cpp/yt/misc/unittests/ya.make
index 5a071c887c4..63df71fb832 100644
--- a/library/cpp/yt/misc/unittests/ya.make
+++ b/library/cpp/yt/misc/unittests/ya.make
@@ -3,8 +3,11 @@ GTEST(unittester-library-misc)
INCLUDE(${ARCADIA_ROOT}/library/cpp/yt/ya_cpp.make.inc)
SRCS(
+ cast_ut.cpp
+ compare_ut.cpp
enum_ut.cpp
guid_ut.cpp
+ hash_ut.cpp
non_null_ptr_ut.cpp
preprocessor_ut.cpp
strong_typedef_ut.cpp
diff --git a/library/cpp/yt/stockpile/stockpile_linux.cpp b/library/cpp/yt/stockpile/stockpile_linux.cpp
index dafb4e9e8c8..ef0ad590327 100644
--- a/library/cpp/yt/stockpile/stockpile_linux.cpp
+++ b/library/cpp/yt/stockpile/stockpile_linux.cpp
@@ -25,7 +25,7 @@ namespace {
void RunWithFixedBreaks(i64 bufferSize, TDuration period)
{
auto returnCode = -::madvise(nullptr, bufferSize, MADV_STOCKPILE);
- YT_LOG_DEBUG_IF(returnCode, "System call \"madvise\" returned %v", strerror(returnCode));
+ YT_LOG_DEBUG_IF(returnCode, "System call \"madvise\" failed: %v", strerror(returnCode));
Sleep(period);
}
@@ -33,7 +33,7 @@ void RunWithCappedLoad(i64 bufferSize, TDuration period)
{
auto started = GetApproximateCpuInstant();
auto returnCode = -::madvise(nullptr, bufferSize, MADV_STOCKPILE);
- YT_LOG_DEBUG_IF(returnCode, "System call \"madvise\" returned %v", strerror(returnCode));
+ YT_LOG_DEBUG_IF(returnCode, "System call \"madvise\" failed: %v", strerror(returnCode));
auto duration = CpuDurationToDuration(GetApproximateCpuInstant() - started);
if (duration < period) {
@@ -48,7 +48,7 @@ std::pair<i64, TDuration> RunWithBackoffs(
i64 pageSize)
{
int returnCode = -::madvise(nullptr, adjustedBufferSize, MADV_STOCKPILE);
- YT_LOG_DEBUG_IF(returnCode, "System call \"madvise\" returned %v", strerror(returnCode));
+ YT_LOG_DEBUG_IF(returnCode, "System call \"madvise\" failed: %v", strerror(returnCode));
switch(returnCode) {
case 0:
diff --git a/library/cpp/yt/system/exit-inl.h b/library/cpp/yt/system/exit-inl.h
new file mode 100644
index 00000000000..e4fe8ea5940
--- /dev/null
+++ b/library/cpp/yt/system/exit-inl.h
@@ -0,0 +1,32 @@
+#ifndef EXIT_INL_H_
+#error "Direct inclusion of this file is not allowed, include exit.h"
+// For the sake of sane code completion.
+#include "exit.h"
+#endif
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class E>
+ requires std::is_enum_v<E>
+[[noreturn]] void AbortProcessSilently(E exitCode)
+{
+ AbortProcessSilently(ToUnderlying(exitCode));
+}
+
+template <class E>
+ requires std::is_enum_v<E>
+[[noreturn]] void AbortProcessDramatically(
+ E exitCode,
+ TStringBuf message)
+{
+ AbortProcessDramatically(
+ ToUnderlying(exitCode),
+ TEnumTraits<E>::FindLiteralByValue(exitCode).value_or("<unknown>"),
+ message);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
diff --git a/library/cpp/yt/system/exit.cpp b/library/cpp/yt/system/exit.cpp
index b17f6c5ad73..e4088fe4da2 100644
--- a/library/cpp/yt/system/exit.cpp
+++ b/library/cpp/yt/system/exit.cpp
@@ -4,11 +4,25 @@ namespace NYT {
////////////////////////////////////////////////////////////////////////////////
-void AbortProcess(int exitCode)
+void AbortProcessSilently(int exitCode)
{
_exit(exitCode);
}
+void AbortProcessDramatically(int exitCode, TStringBuf exitCodeStr, TStringBuf message)
+{
+ fprintf(stderr, "\n");
+ if (message) {
+ fprintf(stderr, "*** %s\n", message.data());
+ }
+ fprintf(stderr, "*** Aborting process with exit code %d", exitCode);
+ if (exitCodeStr) {
+ fprintf(stderr, " (%s)", exitCodeStr.data());
+ }
+ fprintf(stderr, "\n");
+ _exit(exitCode);
+}
+
////////////////////////////////////////////////////////////////////////////////
} // namespace NYT
diff --git a/library/cpp/yt/system/exit.h b/library/cpp/yt/system/exit.h
index 6c009fff901..e06c0bd3ddd 100644
--- a/library/cpp/yt/system/exit.h
+++ b/library/cpp/yt/system/exit.h
@@ -2,6 +2,8 @@
#include <library/cpp/yt/misc/enum.h>
+#include <type_traits>
+
namespace NYT {
////////////////////////////////////////////////////////////////////////////////
@@ -14,9 +16,33 @@ DEFINE_ENUM(EProcessExitCode,
((OutOfMemory) (9))
);
-//! Invokes _exit to abort the process immediately without calling any cleanup code.
-[[noreturn]] void AbortProcess(int exitCode);
+//! Invokes _exit to abort the process immediately without calling any cleanup code
+//! and without printing any details to stderr.
+[[noreturn]] void AbortProcessSilently(int exitCode);
+
+//! A typed version of #AbortProcessSilently.
+template <class E>
+ requires std::is_enum_v<E>
+[[noreturn]] void AbortProcessSilently(E exitCode);
+
+//! Invokes _exit to abort the process immediately
+//! without calling any cleanup code but printing error message to stderr.
+[[noreturn]] void AbortProcessDramatically(
+ int exitCode,
+ TStringBuf exitCodeStr,
+ TStringBuf message);
+
+//! A typed version of #AbortProcessDramatically.
+template <class E>
+ requires std::is_enum_v<E>
+[[noreturn]] void AbortProcessDramatically(
+ E exitCode,
+ TStringBuf message);
////////////////////////////////////////////////////////////////////////////////
} // namespace NYT
+
+#define EXIT_INL_H_
+#include "exit-inl.h"
+#undef EXIT_INL_H_
diff --git a/library/cpp/yt/system/ya.make b/library/cpp/yt/system/ya.make
index de4516d928e..c60a940dbf2 100644
--- a/library/cpp/yt/system/ya.make
+++ b/library/cpp/yt/system/ya.make
@@ -7,4 +7,8 @@ SRCS(
thread_id.cpp
)
+PEERDIR(
+ library/cpp/yt/misc
+)
+
END()