diff options
author | Alexey Salmin <alexey.salmin@gmail.com> | 2022-02-10 16:49:37 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:49:37 +0300 |
commit | 71af077a5dfe7e9f932a508422c2dac81a57ebc0 (patch) | |
tree | 5d5cb817648f650d76cf1076100726fd9b8448e8 /util | |
parent | 3c5b1607b38f637d2f3313791ed25c2e080d2647 (diff) | |
download | ydb-71af077a5dfe7e9f932a508422c2dac81a57ebc0.tar.gz |
Restoring authorship annotation for Alexey Salmin <alexey.salmin@gmail.com>. Commit 2 of 2.
Diffstat (limited to 'util')
39 files changed, 789 insertions, 789 deletions
diff --git a/util/generic/string.h b/util/generic/string.h index 4e1650ef35..8cd8aa6917 100644 --- a/util/generic/string.h +++ b/util/generic/string.h @@ -842,93 +842,93 @@ public: return this->MutRef(); } - /* - * Following overloads of "operator+" aim to choose the cheapest implementation depending on - * summand types: lvalues, detached rvalues, shared rvalues. - * - * General idea is to use the detached-rvalue argument (left of right) to store the result - * wherever possible. If a buffer in rvalue is large enough this saves a re-allocation. If - * both arguments are rvalues we check which one is detached. If both of them are detached then - * the left argument is obviously preferrable because you won't need to shift the data. - * - * If an rvalue is shared then it's basically the same as lvalue because you cannot use its - * buffer to store the sum. However, we rely on the fact that append() and prepend() are already - * optimized for the shared case and detach the string into the buffer large enough to store - * the sum (compared to the detach+reallocation). This way, if we have only one rvalue argument - * (left or right) then we simply append/prepend into it, without checking if it's detached or - * not. This will be checked inside ReserveAndResize anyway. - * - * If both arguments cannot be used to store the sum (e.g. two lvalues) then we fall back to the - * Join function that constructs a resulting string in the new buffer with the minimum overhead: - * malloc + memcpy + memcpy. - */ - + /* + * Following overloads of "operator+" aim to choose the cheapest implementation depending on + * summand types: lvalues, detached rvalues, shared rvalues. + * + * General idea is to use the detached-rvalue argument (left of right) to store the result + * wherever possible. If a buffer in rvalue is large enough this saves a re-allocation. If + * both arguments are rvalues we check which one is detached. If both of them are detached then + * the left argument is obviously preferrable because you won't need to shift the data. + * + * If an rvalue is shared then it's basically the same as lvalue because you cannot use its + * buffer to store the sum. However, we rely on the fact that append() and prepend() are already + * optimized for the shared case and detach the string into the buffer large enough to store + * the sum (compared to the detach+reallocation). This way, if we have only one rvalue argument + * (left or right) then we simply append/prepend into it, without checking if it's detached or + * not. This will be checked inside ReserveAndResize anyway. + * + * If both arguments cannot be used to store the sum (e.g. two lvalues) then we fall back to the + * Join function that constructs a resulting string in the new buffer with the minimum overhead: + * malloc + memcpy + memcpy. + */ + friend TBasicString operator+(TBasicString&& s1, const TBasicString& s2) Y_WARN_UNUSED_RESULT { s1 += s2; - return std::move(s1); + return std::move(s1); } friend TBasicString operator+(const TBasicString& s1, TBasicString&& s2) Y_WARN_UNUSED_RESULT { - s2.prepend(s1); - return std::move(s2); - } - + s2.prepend(s1); + return std::move(s2); + } + friend TBasicString operator+(TBasicString&& s1, TBasicString&& s2) Y_WARN_UNUSED_RESULT { #if 0 && !defined(TSTRING_IS_STD_STRING) - if (!s1.IsDetached() && s2.IsDetached()) { - s2.prepend(s1); - return std::move(s2); - } + if (!s1.IsDetached() && s2.IsDetached()) { + s2.prepend(s1); + return std::move(s2); + } #endif s1 += s2; - return std::move(s1); + return std::move(s1); } friend TBasicString operator+(TBasicString&& s1, const TBasicStringBuf<TCharType, TTraits> s2) Y_WARN_UNUSED_RESULT { s1 += s2; - return std::move(s1); + return std::move(s1); } friend TBasicString operator+(TBasicString&& s1, const TCharType* s2) Y_WARN_UNUSED_RESULT { s1 += s2; - return std::move(s1); + return std::move(s1); } friend TBasicString operator+(TBasicString&& s1, TCharType s2) Y_WARN_UNUSED_RESULT { - s1 += s2; - return std::move(s1); - } - + s1 += s2; + return std::move(s1); + } + friend TBasicString operator+(TExplicitType<TCharType> ch, const TBasicString& s) Y_WARN_UNUSED_RESULT { return Join(TCharType(ch), s); } friend TBasicString operator+(const TBasicString& s1, const TBasicString& s2) Y_WARN_UNUSED_RESULT { - return Join(s1, s2); - } - + return Join(s1, s2); + } + friend TBasicString operator+(const TBasicString& s1, const TBasicStringBuf<TCharType, TTraits> s2) Y_WARN_UNUSED_RESULT { - return Join(s1, s2); - } - + return Join(s1, s2); + } + friend TBasicString operator+(const TBasicString& s1, const TCharType* s2) Y_WARN_UNUSED_RESULT { - return Join(s1, s2); - } - + return Join(s1, s2); + } + friend TBasicString operator+(const TBasicString& s1, TCharType s2) Y_WARN_UNUSED_RESULT { return Join(s1, TBasicStringBuf<TCharType, TTraits>(&s2, 1)); - } - + } + friend TBasicString operator+(const TCharType* s1, TBasicString&& s2) Y_WARN_UNUSED_RESULT { - s2.prepend(s1); - return std::move(s2); - } - + s2.prepend(s1); + return std::move(s2); + } + friend TBasicString operator+(const TBasicStringBuf<TCharType, TTraits> s1, TBasicString&& s2) Y_WARN_UNUSED_RESULT { - s2.prepend(s1); - return std::move(s2); - } - + s2.prepend(s1); + return std::move(s2); + } + friend TBasicString operator+(const TBasicStringBuf<TCharType, TTraits> s1, const TBasicString& s2) Y_WARN_UNUSED_RESULT { return Join(s1, s2); } diff --git a/util/network/sock_ut.cpp b/util/network/sock_ut.cpp index 232aeca2e5..fd8c783747 100644 --- a/util/network/sock_ut.cpp +++ b/util/network/sock_ut.cpp @@ -3,8 +3,8 @@ #include <library/cpp/testing/unittest/registar.h> #include <library/cpp/threading/future/legacy_future.h> -#include <util/system/fs.h> - +#include <util/system/fs.h> + Y_UNIT_TEST_SUITE(TSocketTest) { Y_UNIT_TEST(InetDgramTest) { char buf[256]; @@ -68,8 +68,8 @@ Y_UNIT_TEST_SUITE(TSocketTest) { const char* localServerSockName = "./serv_sock"; const char* localClientSockName = "./cli_sock"; RunLocalDgramTest(localServerSockName, localClientSockName); - NFs::Remove(localServerSockName); - NFs::Remove(localClientSockName); + NFs::Remove(localServerSockName); + NFs::Remove(localClientSockName); } template <class A, class S> @@ -162,7 +162,7 @@ Y_UNIT_TEST_SUITE(TSocketTest) { Y_UNIT_TEST(LocalStreamTest) { const char* localServerSockName = "./serv_sock2"; RunLocalStreamTest(localServerSockName); - NFs::Remove(localServerSockName); + NFs::Remove(localServerSockName); } } diff --git a/util/network/socket.cpp b/util/network/socket.cpp index 32756f503e..4f6e804346 100644 --- a/util/network/socket.cpp +++ b/util/network/socket.cpp @@ -1255,43 +1255,43 @@ void ShutDown(SOCKET s, int mode) { ythrow TSystemError() << "shutdown socket error"; } } - + extern "C" bool IsReusePortAvailable() { -// SO_REUSEPORT is always defined for linux builds, see SetReusePort() implementation above -#if defined(SO_REUSEPORT) - - class TCtx { - public: - TCtx() { - TSocketHolder sock(::socket(AF_INET, SOCK_STREAM, 0)); - const int e1 = errno; - if (sock == INVALID_SOCKET) { - ythrow TSystemError(e1) << "Cannot create AF_INET socket"; - } - int val; - const int ret = GetSockOpt(sock, SOL_SOCKET, SO_REUSEPORT, val); - const int e2 = errno; - if (ret == 0) { - Flag_ = true; - } else { - if (e2 == ENOPROTOOPT) { - Flag_ = false; - } else { - ythrow TSystemError(e2) << "Unexpected error in getsockopt"; - } - } - } - - static inline const TCtx* Instance() noexcept { - return Singleton<TCtx>(); - } - - public: - bool Flag_; - }; - - return TCtx::Instance()->Flag_; -#else - return false; -#endif -} +// SO_REUSEPORT is always defined for linux builds, see SetReusePort() implementation above +#if defined(SO_REUSEPORT) + + class TCtx { + public: + TCtx() { + TSocketHolder sock(::socket(AF_INET, SOCK_STREAM, 0)); + const int e1 = errno; + if (sock == INVALID_SOCKET) { + ythrow TSystemError(e1) << "Cannot create AF_INET socket"; + } + int val; + const int ret = GetSockOpt(sock, SOL_SOCKET, SO_REUSEPORT, val); + const int e2 = errno; + if (ret == 0) { + Flag_ = true; + } else { + if (e2 == ENOPROTOOPT) { + Flag_ = false; + } else { + ythrow TSystemError(e2) << "Unexpected error in getsockopt"; + } + } + } + + static inline const TCtx* Instance() noexcept { + return Singleton<TCtx>(); + } + + public: + bool Flag_; + }; + + return TCtx::Instance()->Flag_; +#else + return false; +#endif +} diff --git a/util/network/socket.h b/util/network/socket.h index b560e47391..40c8648b40 100644 --- a/util/network/socket.h +++ b/util/network/socket.h @@ -136,11 +136,11 @@ ESocketReadStatus HasSocketDataToRead(SOCKET s); **/ bool HasLocalAddress(SOCKET socket); -/** - * Runtime check if current kernel supports SO_REUSEPORT option. - **/ +/** + * Runtime check if current kernel supports SO_REUSEPORT option. + **/ extern "C" bool IsReusePortAvailable(); - + bool IsNonBlock(SOCKET fd); void SetNonBlock(SOCKET fd, bool nonBlock = true); diff --git a/util/network/socket_ut.cpp b/util/network/socket_ut.cpp index 5406312fe9..6b20e11f70 100644 --- a/util/network/socket_ut.cpp +++ b/util/network/socket_ut.cpp @@ -9,11 +9,11 @@ #include <ctime> -#ifdef _linux_ +#ifdef _linux_ #include <linux/version.h> #include <sys/utsname.h> -#endif - +#endif + class TSockTest: public TTestBase { UNIT_TEST_SUITE(TSockTest); UNIT_TEST(TestSock); @@ -25,7 +25,7 @@ class TSockTest: public TTestBase { UNIT_TEST(TestNetworkResolutionErrorMessage); UNIT_TEST(TestBrokenPipe); UNIT_TEST(TestClose); - UNIT_TEST(TestReusePortAvailCheck); + UNIT_TEST(TestReusePortAvailCheck); UNIT_TEST_SUITE_END(); public: @@ -36,7 +36,7 @@ public: void TestNetworkResolutionErrorMessage(); void TestBrokenPipe(); void TestClose(); - void TestReusePortAvailCheck(); + void TestReusePortAvailCheck(); }; UNIT_TEST_SUITE_REGISTRATION(TSockTest); @@ -186,32 +186,32 @@ void TSockTest::TestClose() { } void TSockTest::TestReusePortAvailCheck() { -#if defined _linux_ - utsname sysInfo; - Y_VERIFY(!uname(&sysInfo), "Error while call uname: %s", LastSystemErrorText()); - TStringBuf release(sysInfo.release); - release = release.substr(0, release.find_first_not_of(".0123456789")); - int v1 = FromString<int>(release.NextTok('.')); - int v2 = FromString<int>(release.NextTok('.')); - int v3 = FromString<int>(release.NextTok('.')); - int linuxVersionCode = KERNEL_VERSION(v1, v2, v3); - if (linuxVersionCode >= KERNEL_VERSION(3, 9, 1)) { - // new kernels support SO_REUSEPORT - UNIT_ASSERT(true == IsReusePortAvailable()); - UNIT_ASSERT(true == IsReusePortAvailable()); - } else { - // older kernels may or may not support SO_REUSEPORT - // just check that it doesn't crash or throw - (void)IsReusePortAvailable(); - (void)IsReusePortAvailable(); - } -#else - // check that it doesn't crash or throw - (void)IsReusePortAvailable(); - (void)IsReusePortAvailable(); -#endif -} - +#if defined _linux_ + utsname sysInfo; + Y_VERIFY(!uname(&sysInfo), "Error while call uname: %s", LastSystemErrorText()); + TStringBuf release(sysInfo.release); + release = release.substr(0, release.find_first_not_of(".0123456789")); + int v1 = FromString<int>(release.NextTok('.')); + int v2 = FromString<int>(release.NextTok('.')); + int v3 = FromString<int>(release.NextTok('.')); + int linuxVersionCode = KERNEL_VERSION(v1, v2, v3); + if (linuxVersionCode >= KERNEL_VERSION(3, 9, 1)) { + // new kernels support SO_REUSEPORT + UNIT_ASSERT(true == IsReusePortAvailable()); + UNIT_ASSERT(true == IsReusePortAvailable()); + } else { + // older kernels may or may not support SO_REUSEPORT + // just check that it doesn't crash or throw + (void)IsReusePortAvailable(); + (void)IsReusePortAvailable(); + } +#else + // check that it doesn't crash or throw + (void)IsReusePortAvailable(); + (void)IsReusePortAvailable(); +#endif +} + class TPollTest: public TTestBase { UNIT_TEST_SUITE(TPollTest); UNIT_TEST(TestPollInOut); diff --git a/util/stream/buffer.cpp b/util/stream/buffer.cpp index ad4212f0cd..2facece4ea 100644 --- a/util/stream/buffer.cpp +++ b/util/stream/buffer.cpp @@ -30,10 +30,10 @@ public: Data_.Append((const char*)buf, len); } - inline void DoWriteC(char c) { - Data_.Append(c); - } - + inline void DoWriteC(char c) { + Data_.Append(c); + } + inline TBuffer& Buffer() const noexcept { return Data_; } @@ -87,10 +87,10 @@ void TBufferOutput::DoWrite(const void* buf, size_t len) { Impl_->DoWrite(buf, len); } -void TBufferOutput::DoWriteC(char c) { - Impl_->DoWriteC(c); -} - +void TBufferOutput::DoWriteC(char c) { + Impl_->DoWriteC(c); +} + TBufferInput::TBufferInput(const TBuffer& buffer) : Buf_(buffer) , Readed_(0) diff --git a/util/stream/buffer.h b/util/stream/buffer.h index 2e0ba9144f..9dc99dbe49 100644 --- a/util/stream/buffer.h +++ b/util/stream/buffer.h @@ -49,7 +49,7 @@ private: size_t DoNext(void** ptr) override; void DoUndo(size_t len) override; void DoWrite(const void* buf, size_t len) override; - void DoWriteC(char c) override; + void DoWriteC(char c) override; private: THolder<TImpl> Impl_; diff --git a/util/stream/buffer_ut.cpp b/util/stream/buffer_ut.cpp index 84ba5d3f48..3494696190 100644 --- a/util/stream/buffer_ut.cpp +++ b/util/stream/buffer_ut.cpp @@ -4,8 +4,8 @@ #include <util/generic/buffer.h> -#include <cstring> - +#include <cstring> + #include "str.h" Y_UNIT_TEST_SUITE(TBufferTest) { @@ -32,7 +32,7 @@ Y_UNIT_TEST_SUITE(TBufferTest) { UNIT_ASSERT_VALUES_EQUAL(input.ReadTo(tmp, 'z'), 7); UNIT_ASSERT_VALUES_EQUAL(tmp, "4567890"); } - + Y_UNIT_TEST(WriteViaNextAndUndo) { TBuffer buffer; TBufferOutput output(buffer); @@ -59,27 +59,27 @@ Y_UNIT_TEST_SUITE(TBufferTest) { } Y_UNIT_TEST(Write) { - TBuffer buffer; - TBufferOutput output(buffer); + TBuffer buffer; + TBufferOutput output(buffer); output << "1" << "22" << "333" << "4444" << "55555"; - + UNIT_ASSERT(0 == memcmp(buffer.data(), "1" "22" "333" "4444" "55555", buffer.size())); - } - + } + Y_UNIT_TEST(WriteChars) { - TBuffer buffer; - TBufferOutput output(buffer); - output << '1' << '2' << '3' << '4' << '5' << '6' << '7' << '8' << '9' << '0'; - + TBuffer buffer; + TBufferOutput output(buffer); + output << '1' << '2' << '3' << '4' << '5' << '6' << '7' << '8' << '9' << '0'; + UNIT_ASSERT(0 == memcmp(buffer.data(), "1234567890", buffer.size())); - } + } } diff --git a/util/stream/buffered.cpp b/util/stream/buffered.cpp index 229cb78423..a00e592e1c 100644 --- a/util/stream/buffered.cpp +++ b/util/stream/buffered.cpp @@ -193,16 +193,16 @@ public: using TPart = IOutputStream::TPart; - alignas(TPart) char data[2 * sizeof(TPart)]; - TPart* parts = reinterpret_cast<TPart*>(data); + alignas(TPart) char data[2 * sizeof(TPart)]; + TPart* parts = reinterpret_cast<TPart*>(data); TPart* end = parts; if (stored) { - new (end++) TPart(Buf(), stored); + new (end++) TPart(Buf(), stored); } if (write_from_buf) { - new (end++) TPart(buf, write_from_buf); + new (end++) TPart(buf, write_from_buf); } Slave_->Write(parts, end - parts); @@ -217,16 +217,16 @@ public: } } - inline void Write(char c) { - if (Y_UNLIKELY(MemOut_.Avail() == 0)) { - Slave_->Write(Buf(), Stored()); - OnBufferExhausted(); - Reset(); - } - - MemOut_.Write(c); - } - + inline void Write(char c) { + if (Y_UNLIKELY(MemOut_.Avail() == 0)) { + Slave_->Write(Buf(), Stored()); + OnBufferExhausted(); + Reset(); + } + + MemOut_.Write(c); + } + inline void SetFlushPropagateMode(bool mode) noexcept { PropagateFlush_ = mode; } @@ -382,11 +382,11 @@ void TBufferedOutputBase::DoWrite(const void* data, size_t len) { Impl_->Write(data, len); } -void TBufferedOutputBase::DoWriteC(char c) { +void TBufferedOutputBase::DoWriteC(char c) { Y_ENSURE(Impl_.Get(), "cannot write to finished stream"); Impl_->Write(c); -} - +} + void TBufferedOutputBase::DoFlush() { if (Impl_.Get()) { Impl_->Flush(); diff --git a/util/stream/buffered.h b/util/stream/buffered.h index f7e4c03262..0847186141 100644 --- a/util/stream/buffered.h +++ b/util/stream/buffered.h @@ -114,7 +114,7 @@ protected: size_t DoNext(void** ptr) override; void DoUndo(size_t len) override; void DoWrite(const void* data, size_t len) override; - void DoWriteC(char c) override; + void DoWriteC(char c) override; void DoFlush() override; void DoFinish() override; diff --git a/util/stream/buffered_ut.cpp b/util/stream/buffered_ut.cpp index 075a388477..41d2fc3030 100644 --- a/util/stream/buffered_ut.cpp +++ b/util/stream/buffered_ut.cpp @@ -55,24 +55,24 @@ Y_UNIT_TEST_SUITE(TestBufferedIO) { b.Write("1", 1); b.Write("12", 2); - b.Finish(); + b.Finish(); UNIT_ASSERT_VALUES_EQUAL(s, "112"); } Y_UNIT_TEST(Test4) { - TString s; - - auto&& b = TBuffered<TStringOutput>(1, s); - - b.Write('1'); - b.Write('2'); - b.Write('3'); - b.Finish(); - - UNIT_ASSERT_VALUES_EQUAL(s, "123"); - } - + TString s; + + auto&& b = TBuffered<TStringOutput>(1, s); + + b.Write('1'); + b.Write('2'); + b.Write('3'); + b.Finish(); + + UNIT_ASSERT_VALUES_EQUAL(s, "123"); + } + template <class TOut> inline void DoGenAndWrite(TOut&& output, TString& str) { TMersenne<ui64> r; diff --git a/util/stream/mem.cpp b/util/stream/mem.cpp index 996e58b188..22a3339e27 100644 --- a/util/stream/mem.cpp +++ b/util/stream/mem.cpp @@ -58,8 +58,8 @@ void TMemoryOutput::DoWrite(const void* buf, size_t len) { memcpy(Buf_, buf, len); Buf_ = end; } - -void TMemoryOutput::DoWriteC(char c) { + +void TMemoryOutput::DoWriteC(char c) { Y_ENSURE(Buf_ < End_, TStringBuf("memory output stream exhausted")); - *Buf_++ = c; -} + *Buf_++ = c; +} diff --git a/util/stream/mem.h b/util/stream/mem.h index 5d887f4842..18a5d46772 100644 --- a/util/stream/mem.h +++ b/util/stream/mem.h @@ -177,7 +177,7 @@ private: size_t DoNext(void** ptr) override; void DoUndo(size_t len) override; void DoWrite(const void* buf, size_t len) override; - void DoWriteC(char c) override; + void DoWriteC(char c) override; protected: char* Buf_; diff --git a/util/stream/mem_ut.cpp b/util/stream/mem_ut.cpp index 398db6598b..f388ae66ac 100644 --- a/util/stream/mem_ut.cpp +++ b/util/stream/mem_ut.cpp @@ -13,7 +13,7 @@ Y_UNIT_TEST_SUITE(TestMemIO) { UNIT_ASSERT_VALUES_EQUAL(in.ReadTo(t, 'z'), 5); UNIT_ASSERT_VALUES_EQUAL(t, "89abc"); } - + Y_UNIT_TEST(NextAndUndo) { char buffer[20]; TMemoryOutput output(buffer, sizeof(buffer)); @@ -51,28 +51,28 @@ Y_UNIT_TEST_SUITE(TestMemIO) { } Y_UNIT_TEST(Write) { - char buffer[20]; - TMemoryOutput output(buffer, sizeof(buffer)); + char buffer[20]; + TMemoryOutput output(buffer, sizeof(buffer)); output << "1" << "22" << "333" << "4444" << "55555"; - + const char* const result = "1" "22" "333" "4444" "55555"; - UNIT_ASSERT(0 == memcmp(buffer, result, strlen(result))); - } - + UNIT_ASSERT(0 == memcmp(buffer, result, strlen(result))); + } + Y_UNIT_TEST(WriteChars) { - char buffer[20]; - TMemoryOutput output(buffer, sizeof(buffer)); - output << '1' << '2' << '3' << '4' << '5' << '6' << '7' << '8' << '9' << '0'; - - const char* const result = "1234567890"; - UNIT_ASSERT(0 == memcmp(buffer, result, strlen(result))); - } + char buffer[20]; + TMemoryOutput output(buffer, sizeof(buffer)); + output << '1' << '2' << '3' << '4' << '5' << '6' << '7' << '8' << '9' << '0'; + + const char* const result = "1234567890"; + UNIT_ASSERT(0 == memcmp(buffer, result, strlen(result))); + } } diff --git a/util/stream/output.cpp b/util/stream/output.cpp index 82dd81a207..db81b81b70 100644 --- a/util/stream/output.cpp +++ b/util/stream/output.cpp @@ -48,10 +48,10 @@ void IOutputStream::DoWriteV(const TPart* parts, size_t count) { } } -void IOutputStream::DoWriteC(char ch) { - DoWrite(&ch, 1); -} - +void IOutputStream::DoWriteC(char ch) { + DoWrite(&ch, 1); +} + template <> void Out<wchar16>(IOutputStream& o, wchar16 ch) { const wchar32 w32ch = ReadSymbol(&ch, &ch + 1); diff --git a/util/stream/output.h b/util/stream/output.h index da6f052d5d..00eef50b95 100644 --- a/util/stream/output.h +++ b/util/stream/output.h @@ -104,7 +104,7 @@ public: * @param ch Character to write. */ inline void Write(char ch) { - DoWriteC(ch); + DoWriteC(ch); } /** @@ -148,13 +148,13 @@ protected: virtual void DoWriteV(const TPart* parts, size_t count); /** - * Writes a single character into this stream. Can be overridden with a faster implementation. - * - * @param ch Character to write. - */ - virtual void DoWriteC(char ch); - - /** + * Writes a single character into this stream. Can be overridden with a faster implementation. + * + * @param ch Character to write. + */ + virtual void DoWriteC(char ch); + + /** * Flushes this stream's buffer, if any. * * @throws yexception If IO error occurs. diff --git a/util/stream/str.cpp b/util/stream/str.cpp index 2d441db2f6..13f0e8ef28 100644 --- a/util/stream/str.cpp +++ b/util/stream/str.cpp @@ -37,8 +37,8 @@ void TStringOutput::DoWrite(const void* buf, size_t len) { S_->append((const char*)buf, len); } -void TStringOutput::DoWriteC(char c) { +void TStringOutput::DoWriteC(char c) { S_->push_back(c); -} - +} + TStringStream::~TStringStream() = default; diff --git a/util/stream/str.h b/util/stream/str.h index 651bab19ce..028bd572c0 100644 --- a/util/stream/str.h +++ b/util/stream/str.h @@ -98,7 +98,7 @@ protected: size_t DoNext(void** ptr) override; void DoUndo(size_t len) override; void DoWrite(const void* buf, size_t len) override; - void DoWriteC(char c) override; + void DoWriteC(char c) override; private: TString* S_; diff --git a/util/stream/str_ut.cpp b/util/stream/str_ut.cpp index cde603b86b..fc6b46c31a 100644 --- a/util/stream/str_ut.cpp +++ b/util/stream/str_ut.cpp @@ -87,7 +87,7 @@ Y_UNIT_TEST_SUITE(TStringInputOutputTest) { UNIT_ASSERT_VALUES_EQUAL(in0.ReadTo(t, 'z'), 5); UNIT_ASSERT_VALUES_EQUAL(t, "89abc"); } - + Y_UNIT_TEST(WriteViaNextAndUndo) { TString str1; TStringOutput output(str1); @@ -114,28 +114,28 @@ Y_UNIT_TEST_SUITE(TStringInputOutputTest) { } Y_UNIT_TEST(Write) { - TString str; - TStringOutput output(str); + TString str; + TStringOutput output(str); output << "1" << "22" << "333" << "4444" << "55555"; - + UNIT_ASSERT_STRINGS_EQUAL(str, "1" "22" "333" "4444" "55555"); - } - + } + Y_UNIT_TEST(WriteChars) { - TString str; - TStringOutput output(str); - output << '1' << '2' << '3' << '4' << '5' << '6' << '7' << '8' << '9' << '0'; - - UNIT_ASSERT_STRINGS_EQUAL(str, "1234567890"); - } + TString str; + TStringOutput output(str); + output << '1' << '2' << '3' << '4' << '5' << '6' << '7' << '8' << '9' << '0'; + + UNIT_ASSERT_STRINGS_EQUAL(str, "1234567890"); + } Y_UNIT_TEST(MoveConstructor) { TString str; diff --git a/util/string/benchmark/join/main.cpp b/util/string/benchmark/join/main.cpp index 94466069d4..1a8633d3a8 100644 --- a/util/string/benchmark/join/main.cpp +++ b/util/string/benchmark/join/main.cpp @@ -1,70 +1,70 @@ #include <library/cpp/testing/benchmark/bench.h> - -#include <util/generic/function.h> -#include <util/generic/singleton.h> -#include <util/generic/vector.h> -#include <util/generic/xrange.h> -#include <util/random/fast.h> -#include <util/string/cast.h> -#include <util/string/join.h> - -namespace { - // This class assigns random values to variadic lists of variables of different types. - // It can be used to randomize a tuple via Apply() (arcadia version of std::apply). - class TRandomizer { - public: - TRandomizer(ui64 seed) - : Prng(seed) - { - } - - void Randomize(ui16& i) { - i = static_cast<ui16>(Prng.GenRand()); - } - - void Randomize(ui32& i) { - i = static_cast<ui32>(Prng.GenRand()); - } - - void Randomize(double& d) { - d = Prng.GenRandReal4() + Prng.Uniform(Max<ui16>()); - } - - void Randomize(TString& s) { - s = ::ToString(Prng.GenRand()); - } - + +#include <util/generic/function.h> +#include <util/generic/singleton.h> +#include <util/generic/vector.h> +#include <util/generic/xrange.h> +#include <util/random/fast.h> +#include <util/string/cast.h> +#include <util/string/join.h> + +namespace { + // This class assigns random values to variadic lists of variables of different types. + // It can be used to randomize a tuple via Apply() (arcadia version of std::apply). + class TRandomizer { + public: + TRandomizer(ui64 seed) + : Prng(seed) + { + } + + void Randomize(ui16& i) { + i = static_cast<ui16>(Prng.GenRand()); + } + + void Randomize(ui32& i) { + i = static_cast<ui32>(Prng.GenRand()); + } + + void Randomize(double& d) { + d = Prng.GenRandReal4() + Prng.Uniform(Max<ui16>()); + } + + void Randomize(TString& s) { + s = ::ToString(Prng.GenRand()); + } + template <typename T, typename... TArgs> - void Randomize(T& t, TArgs&... args) { - Randomize(t); - Randomize(args...); - } - - private: - TFastRng<ui64> Prng; - }; - + void Randomize(T& t, TArgs&... args) { + Randomize(t); + Randomize(args...); + } + + private: + TFastRng<ui64> Prng; + }; + template <size_t N, typename... T> - struct TExamplesHolder { + struct TExamplesHolder { using TExamples = TVector<std::tuple<T...>>; - TExamples Examples; - - TExamplesHolder() - : Examples(N) - { - TRandomizer r{N * sizeof(typename TExamples::value_type) * 42}; - for (auto& x : Examples) { + TExamples Examples; + + TExamplesHolder() + : Examples(N) + { + TRandomizer r{N * sizeof(typename TExamples::value_type) * 42}; + for (auto& x : Examples) { Apply([&r](T&... t) { r.Randomize(t...); }, x); - } - } - }; - + } + } + }; + template <typename... TArgs> - TString JoinTuple(std::tuple<TArgs...> t) { - return Apply([](TArgs... x) -> TString { return Join("-", x...); }, t); - } -} - + TString JoinTuple(std::tuple<TArgs...> t) { + return Apply([](TArgs... x) -> TString { return Join("-", x...); }, t); + } +} + #define DEFINE_BENCHMARK(count, types, ...) \ Y_CPU_BENCHMARK(Join_##count##_##types, iface) { \ const auto& examples = Default<TExamplesHolder<count, __VA_ARGS__>>().Examples; \ @@ -74,22 +74,22 @@ namespace { Y_DO_NOT_OPTIMIZE_AWAY(JoinTuple(e)); \ } \ } \ - } - -DEFINE_BENCHMARK(100, SS, TString, TString); -DEFINE_BENCHMARK(100, SSS, TString, TString, TString); -DEFINE_BENCHMARK(100, SSSSS, TString, TString, TString, TString, TString); - -DEFINE_BENCHMARK(100, ss, ui16, ui16); -DEFINE_BENCHMARK(100, SsS, TString, ui16, TString); -DEFINE_BENCHMARK(100, SsSsS, TString, ui16, TString, ui16, TString); - -DEFINE_BENCHMARK(100, ii, ui32, ui32); -DEFINE_BENCHMARK(100, SiS, TString, ui32, TString); -DEFINE_BENCHMARK(100, SiSiS, TString, ui32, TString, ui32, TString); - -DEFINE_BENCHMARK(100, dd, double, double); -DEFINE_BENCHMARK(100, SdS, TString, double, TString); -DEFINE_BENCHMARK(100, SdSdS, TString, double, TString, double, TString); - -#undef DEFINE_BENCHMARK + } + +DEFINE_BENCHMARK(100, SS, TString, TString); +DEFINE_BENCHMARK(100, SSS, TString, TString, TString); +DEFINE_BENCHMARK(100, SSSSS, TString, TString, TString, TString, TString); + +DEFINE_BENCHMARK(100, ss, ui16, ui16); +DEFINE_BENCHMARK(100, SsS, TString, ui16, TString); +DEFINE_BENCHMARK(100, SsSsS, TString, ui16, TString, ui16, TString); + +DEFINE_BENCHMARK(100, ii, ui32, ui32); +DEFINE_BENCHMARK(100, SiS, TString, ui32, TString); +DEFINE_BENCHMARK(100, SiSiS, TString, ui32, TString, ui32, TString); + +DEFINE_BENCHMARK(100, dd, double, double); +DEFINE_BENCHMARK(100, SdS, TString, double, TString); +DEFINE_BENCHMARK(100, SdSdS, TString, double, TString, double, TString); + +#undef DEFINE_BENCHMARK diff --git a/util/string/benchmark/join/metrics/main.py b/util/string/benchmark/join/metrics/main.py index 60eb0e470f..1ed5014808 100644 --- a/util/string/benchmark/join/metrics/main.py +++ b/util/string/benchmark/join/metrics/main.py @@ -1,5 +1,5 @@ -import yatest.common as yc - - -def test_export_metrics(metrics): +import yatest.common as yc + + +def test_export_metrics(metrics): metrics.set_benchmark(yc.execute_benchmark('util/string/benchmark/join/join', threads=8)) diff --git a/util/string/benchmark/join/metrics/ya.make b/util/string/benchmark/join/metrics/ya.make index bdbf806aa2..08ff3a149f 100644 --- a/util/string/benchmark/join/metrics/ya.make +++ b/util/string/benchmark/join/metrics/ya.make @@ -1,21 +1,21 @@ -OWNER( - salmin - g:util -) +OWNER( + salmin + g:util +) SUBSCRIBER(g:util-subscribers) - + PY2TEST() - + SIZE(LARGE) - -TAG( + +TAG( ya:force_sandbox - sb:intel_e5_2660v1 + sb:intel_e5_2660v1 ya:fat -) - +) + TEST_SRCS(main.py) - + DEPENDS(util/string/benchmark/join) - -END() + +END() diff --git a/util/string/benchmark/join/ya.make b/util/string/benchmark/join/ya.make index 6ba2d2dadb..dfcc1d264e 100644 --- a/util/string/benchmark/join/ya.make +++ b/util/string/benchmark/join/ya.make @@ -1,13 +1,13 @@ Y_BENCHMARK() - -OWNER( - salmin - g:util -) + +OWNER( + salmin + g:util +) SUBSCRIBER(g:util-subscribers) - -SRCS( - main.cpp -) - -END() + +SRCS( + main.cpp +) + +END() diff --git a/util/string/benchmark/ya.make b/util/string/benchmark/ya.make index e6ab78b6f6..266b53c7b3 100644 --- a/util/string/benchmark/ya.make +++ b/util/string/benchmark/ya.make @@ -9,8 +9,8 @@ RECURSE( cast float_to_string float_to_string/metrics - join - join/metrics + join + join/metrics subst_global subst_global/metrics ) diff --git a/util/string/cast.h b/util/string/cast.h index 9c230d1095..90e925c194 100644 --- a/util/string/cast.h +++ b/util/string/cast.h @@ -69,10 +69,10 @@ namespace NPrivate { template <class T> struct TToString<T, false> { static inline TString Cvt(const T& t) { - TString s; - TStringOutput o(s); - o << t; - return s; + TString s; + TStringOutput o(s); + o << t; + return s; } }; } diff --git a/util/string/join.h b/util/string/join.h index a49e6dfe50..b166fad1f3 100644 --- a/util/string/join.h +++ b/util/string/join.h @@ -5,31 +5,31 @@ #include <util/string/cast.h> #include "cast.h" -/* - * Default implementation of AppendToString uses a temporary TString object which is inefficient. You can overload it - * for your type to speed up string joins. If you already have an Out() or operator<<() implementation you can simply - * do the following: - * - * inline void AppendToString(TString& dst, const TMyType& t) { - * TStringOutput o(dst); - * o << t; - * } - * - * Unfortunately we can't do this by default because for some types ToString() is defined while Out() is not. - * For standard types (strings of all kinds and arithmetic types) we don't use a temporary TString in AppendToString(). - */ - +/* + * Default implementation of AppendToString uses a temporary TString object which is inefficient. You can overload it + * for your type to speed up string joins. If you already have an Out() or operator<<() implementation you can simply + * do the following: + * + * inline void AppendToString(TString& dst, const TMyType& t) { + * TStringOutput o(dst); + * o << t; + * } + * + * Unfortunately we can't do this by default because for some types ToString() is defined while Out() is not. + * For standard types (strings of all kinds and arithmetic types) we don't use a temporary TString in AppendToString(). + */ + template <typename TCharType, typename T> -inline std::enable_if_t<!std::is_arithmetic<std::remove_cv_t<T>>::value, void> +inline std::enable_if_t<!std::is_arithmetic<std::remove_cv_t<T>>::value, void> AppendToString(TBasicString<TCharType>& dst, const T& t) { dst.AppendNoAlias(ToString(t)); -} +} template <typename TCharType, typename T> -inline std::enable_if_t<std::is_arithmetic<std::remove_cv_t<T>>::value, void> +inline std::enable_if_t<std::is_arithmetic<std::remove_cv_t<T>>::value, void> AppendToString(TBasicString<TCharType>& dst, const T& t) { - char buf[512]; - dst.append(buf, ToString<std::remove_cv_t<T>>(t, buf, sizeof(buf))); + char buf[512]; + dst.append(buf, ToString<std::remove_cv_t<T>>(t, buf, sizeof(buf))); } template <typename TCharType> @@ -42,59 +42,59 @@ inline void AppendToString(TBasicString<TCharType>& dst, TBasicStringBuf<TCharTy dst.append(t); } -namespace NPrivate { - template <typename T> - inline size_t GetLength(const T&) { - // By default don't pre-allocate space when joining and appending non-string types. - // This code can be extended by estimating stringified length for specific types (e.g. 10 for ui32). - return 0; - } - - template <> - inline size_t GetLength(const TString& s) { - return s.length(); - } - - template <> - inline size_t GetLength(const TStringBuf& s) { - return s.length(); - } - - template <> - inline size_t GetLength(const char* const& s) { +namespace NPrivate { + template <typename T> + inline size_t GetLength(const T&) { + // By default don't pre-allocate space when joining and appending non-string types. + // This code can be extended by estimating stringified length for specific types (e.g. 10 for ui32). + return 0; + } + + template <> + inline size_t GetLength(const TString& s) { + return s.length(); + } + + template <> + inline size_t GetLength(const TStringBuf& s) { + return s.length(); + } + + template <> + inline size_t GetLength(const char* const& s) { return (s ? std::char_traits<char>::length(s) : 0); - } - - inline size_t GetAppendLength(const TStringBuf /*delim*/) { - return 0; - } - - template <typename TFirst, typename... TRest> - size_t GetAppendLength(const TStringBuf delim, const TFirst& f, const TRest&... r) { - return delim.length() + ::NPrivate::GetLength(f) + ::NPrivate::GetAppendLength(delim, r...); - } + } + + inline size_t GetAppendLength(const TStringBuf /*delim*/) { + return 0; + } + + template <typename TFirst, typename... TRest> + size_t GetAppendLength(const TStringBuf delim, const TFirst& f, const TRest&... r) { + return delim.length() + ::NPrivate::GetLength(f) + ::NPrivate::GetAppendLength(delim, r...); + } } template <typename TCharType> inline void AppendJoinNoReserve(TBasicString<TCharType>&, TBasicStringBuf<TCharType>) { -} - +} + template <typename TCharType, typename TFirst, typename... TRest> inline void AppendJoinNoReserve(TBasicString<TCharType>& dst, TBasicStringBuf<TCharType> delim, const TFirst& f, const TRest&... r) { AppendToString(dst, delim); AppendToString(dst, f); - AppendJoinNoReserve(dst, delim, r...); + AppendJoinNoReserve(dst, delim, r...); +} + +template <typename... TValues> +inline void AppendJoin(TString& dst, const TStringBuf delim, const TValues&... values) { + const size_t appendLength = ::NPrivate::GetAppendLength(delim, values...); + if (appendLength > 0) { + dst.reserve(dst.length() + appendLength); + } + AppendJoinNoReserve(dst, delim, values...); } -template <typename... TValues> -inline void AppendJoin(TString& dst, const TStringBuf delim, const TValues&... values) { - const size_t appendLength = ::NPrivate::GetAppendLength(delim, values...); - if (appendLength > 0) { - dst.reserve(dst.length() + appendLength); - } - AppendJoinNoReserve(dst, delim, values...); -} - template <typename TFirst, typename... TRest> inline TString Join(const TStringBuf delim, const TFirst& f, const TRest&... r) { TString ret = ToString(f); @@ -127,8 +127,8 @@ namespace NPrivate { for (TIter pos = beg; ++pos != end;) { AppendJoinNoReserve(out, delim, *pos); } - } - + } + return out; } @@ -241,25 +241,25 @@ constexpr auto MakeRangeJoiner(TStringBuf delim, const std::initializer_list<TVa return MakeRangeJoiner(delim, std::cbegin(data), std::cend(data)); } -/* We force (std::initializer_list<TStringBuf>) input type for (TString) and (const char*) types because: - * # When (std::initializer_list<TString>) is used, TString objects are copied into the initializer_list object. - * Storing TStringBufs instead is faster, even with COW-enabled strings. - * # For (const char*) we calculate length only once and store it in TStringBuf. Otherwise strlen scan would be executed - * in both GetAppendLength and AppendToString. For string literals constant lengths get propagated in compile-time. - * - * This way JoinSeq(",", { s1, s2 }) always does the right thing whatever types s1 and s2 have. - * - * If someone needs to join std::initializer_list<TString> -- it still works because of the TContainer template above. -*/ - +/* We force (std::initializer_list<TStringBuf>) input type for (TString) and (const char*) types because: + * # When (std::initializer_list<TString>) is used, TString objects are copied into the initializer_list object. + * Storing TStringBufs instead is faster, even with COW-enabled strings. + * # For (const char*) we calculate length only once and store it in TStringBuf. Otherwise strlen scan would be executed + * in both GetAppendLength and AppendToString. For string literals constant lengths get propagated in compile-time. + * + * This way JoinSeq(",", { s1, s2 }) always does the right thing whatever types s1 and s2 have. + * + * If someone needs to join std::initializer_list<TString> -- it still works because of the TContainer template above. +*/ + template <typename T> inline std::enable_if_t< !std::is_same<std::decay_t<T>, TString>::value && !std::is_same<std::decay_t<T>, const char*>::value, TString> -JoinSeq(const TStringBuf delim, const std::initializer_list<T>& data) { +JoinSeq(const TStringBuf delim, const std::initializer_list<T>& data) { + return JoinRange(delim, data.begin(), data.end()); +} + +inline TString JoinSeq(const TStringBuf delim, const std::initializer_list<TStringBuf>& data) { return JoinRange(delim, data.begin(), data.end()); } - -inline TString JoinSeq(const TStringBuf delim, const std::initializer_list<TStringBuf>& data) { - return JoinRange(delim, data.begin(), data.end()); -} diff --git a/util/string/join_ut.cpp b/util/string/join_ut.cpp index 74ff515da9..3ed2b2459c 100644 --- a/util/string/join_ut.cpp +++ b/util/string/join_ut.cpp @@ -39,18 +39,18 @@ Y_UNIT_TEST_SUITE(JoinStringTest) { } Y_UNIT_TEST(StrContainerItems) { - // try various overloads and template type arguments - static const char* const result = "1 22 333"; - static const char* const v[] = {"1", "22", "333"}; + // try various overloads and template type arguments + static const char* const result = "1 22 333"; + static const char* const v[] = {"1", "22", "333"}; TVector<const char*> vchar(v, v + sizeof(v) / sizeof(v[0])); TVector<TStringBuf> vbuf(v, v + sizeof(v) / sizeof(v[0])); TVector<TString> vstring(v, v + sizeof(v) / sizeof(v[0])); - - // ranges - UNIT_ASSERT_EQUAL(JoinRange(" ", v, v + 3), result); - UNIT_ASSERT_EQUAL(JoinRange(" ", vchar.begin(), vchar.end()), result); - UNIT_ASSERT_EQUAL(JoinRange(" ", vbuf.begin(), vbuf.end()), result); - UNIT_ASSERT_EQUAL(JoinRange(" ", vstring.begin(), vstring.end()), result); + + // ranges + UNIT_ASSERT_EQUAL(JoinRange(" ", v, v + 3), result); + UNIT_ASSERT_EQUAL(JoinRange(" ", vchar.begin(), vchar.end()), result); + UNIT_ASSERT_EQUAL(JoinRange(" ", vbuf.begin(), vbuf.end()), result); + UNIT_ASSERT_EQUAL(JoinRange(" ", vstring.begin(), vstring.end()), result); { TStringStream stream; stream << MakeRangeJoiner(" ", v, v + 3); @@ -71,11 +71,11 @@ Y_UNIT_TEST_SUITE(JoinStringTest) { stream << MakeRangeJoiner(" ", vstring.begin(), vstring.end()); UNIT_ASSERT_EQUAL(stream.Str(), result); } - - // vectors - UNIT_ASSERT_EQUAL(JoinSeq(" ", vchar), result); - UNIT_ASSERT_EQUAL(JoinSeq(" ", vbuf), result); - UNIT_ASSERT_EQUAL(JoinSeq(" ", vstring), result); + + // vectors + UNIT_ASSERT_EQUAL(JoinSeq(" ", vchar), result); + UNIT_ASSERT_EQUAL(JoinSeq(" ", vbuf), result); + UNIT_ASSERT_EQUAL(JoinSeq(" ", vstring), result); { TStringStream stream; stream << MakeRangeJoiner(" ", vchar); @@ -91,12 +91,12 @@ Y_UNIT_TEST_SUITE(JoinStringTest) { stream << MakeRangeJoiner(" ", vstring); UNIT_ASSERT_EQUAL(stream.Str(), result); } - - // initializer lists with type deduction - UNIT_ASSERT_EQUAL(JoinSeq(" ", {v[0], v[1], v[2]}), result); - UNIT_ASSERT_EQUAL(JoinSeq(" ", {vchar[0], vchar[1], vchar[2]}), result); - UNIT_ASSERT_EQUAL(JoinSeq(" ", {vbuf[0], vbuf[1], vbuf[2]}), result); - UNIT_ASSERT_EQUAL(JoinSeq(" ", {vstring[0], vstring[1], vstring[2]}), result); + + // initializer lists with type deduction + UNIT_ASSERT_EQUAL(JoinSeq(" ", {v[0], v[1], v[2]}), result); + UNIT_ASSERT_EQUAL(JoinSeq(" ", {vchar[0], vchar[1], vchar[2]}), result); + UNIT_ASSERT_EQUAL(JoinSeq(" ", {vbuf[0], vbuf[1], vbuf[2]}), result); + UNIT_ASSERT_EQUAL(JoinSeq(" ", {vstring[0], vstring[1], vstring[2]}), result); { TStringStream stream; stream << MakeRangeJoiner(" ", {v[0], v[1], v[2]}); @@ -117,12 +117,12 @@ Y_UNIT_TEST_SUITE(JoinStringTest) { stream << MakeRangeJoiner(" ", {vstring[0], vstring[1], vstring[2]}); UNIT_ASSERT_EQUAL(stream.Str(), result); } - - // initializer lists with explicit types - UNIT_ASSERT_EQUAL(JoinSeq(" ", std::initializer_list<const char*>{v[0], v[1], v[2]}), result); - UNIT_ASSERT_EQUAL(JoinSeq(" ", std::initializer_list<const char*>{vchar[0], vchar[1], vchar[2]}), result); - UNIT_ASSERT_EQUAL(JoinSeq(" ", std::initializer_list<TStringBuf>{vbuf[0], vbuf[1], vbuf[2]}), result); - UNIT_ASSERT_EQUAL(JoinSeq(" ", std::initializer_list<TString>{vstring[0], vstring[1], vstring[2]}), result); + + // initializer lists with explicit types + UNIT_ASSERT_EQUAL(JoinSeq(" ", std::initializer_list<const char*>{v[0], v[1], v[2]}), result); + UNIT_ASSERT_EQUAL(JoinSeq(" ", std::initializer_list<const char*>{vchar[0], vchar[1], vchar[2]}), result); + UNIT_ASSERT_EQUAL(JoinSeq(" ", std::initializer_list<TStringBuf>{vbuf[0], vbuf[1], vbuf[2]}), result); + UNIT_ASSERT_EQUAL(JoinSeq(" ", std::initializer_list<TString>{vstring[0], vstring[1], vstring[2]}), result); { TStringStream stream; stream << MakeRangeJoiner(" ", std::initializer_list<const char*>{v[0], v[1], v[2]}); @@ -146,8 +146,8 @@ Y_UNIT_TEST_SUITE(JoinStringTest) { // c-style array UNIT_ASSERT_VALUES_EQUAL(JoinSeq(" ", v), result); - } - + } + Y_UNIT_TEST(CustomToString) { TCustomData d1{{1, 2, 3, 4, 5}}; TCustomData d2{{0, -1, -2}}; diff --git a/util/system/direct_io.cpp b/util/system/direct_io.cpp index 8a0d453bdd..f59c54b0cb 100644 --- a/util/system/direct_io.cpp +++ b/util/system/direct_io.cpp @@ -34,10 +34,10 @@ namespace { } else if (linuxVersionCode < KERNEL_VERSION(2, 6, 0)) { Alignment = NSystemInfo::GetPageSize(); } else { - // Default alignment used to be 512, but most modern devices rely on 4k physical blocks. - // 4k alignment works well for both 512 and 4k blocks and doesn't require 512e support in the kernel. - // See IGNIETFERRO-946. - Alignment = 4096; + // Default alignment used to be 512, but most modern devices rely on 4k physical blocks. + // 4k alignment works well for both 512 and 4k blocks and doesn't require 512e support in the kernel. + // See IGNIETFERRO-946. + Alignment = 4096; } #endif } diff --git a/util/system/direct_io_ut.cpp b/util/system/direct_io_ut.cpp index d6f5e26ffa..839c3de7ca 100644 --- a/util/system/direct_io_ut.cpp +++ b/util/system/direct_io_ut.cpp @@ -1,7 +1,7 @@ #include <library/cpp/testing/unittest/registar.h> #include <util/generic/yexception.h> -#include <util/system/fs.h> +#include <util/system/fs.h> #include <util/system/tempfile.h> #include <util/random/random.h> @@ -11,7 +11,7 @@ static const char* FileName_("./test.file"); Y_UNIT_TEST_SUITE(TDirectIoTestSuite) { Y_UNIT_TEST(TestDirectFile) { - TDirectIOBufferedFile file(FileName_, RdWr | Direct | Seq | CreateAlways, 1 << 15); + TDirectIOBufferedFile file(FileName_, RdWr | Direct | Seq | CreateAlways, 1 << 15); TVector<ui64> data((1 << 15) + 1); TVector<ui64> readResult(data.size()); for (auto& i : data) { @@ -31,7 +31,7 @@ Y_UNIT_TEST_SUITE(TDirectIoTestSuite) { } } file.Finish(); - TDirectIOBufferedFile fileNew(FileName_, RdOnly | Direct | Seq | OpenAlways, 1 << 15); + TDirectIOBufferedFile fileNew(FileName_, RdOnly | Direct | Seq | OpenAlways, 1 << 15); for (int i = 0; i < 1000; ++i) { size_t readPos = RandomNumber(data.size()); size_t readCount = RandomNumber(data.size() - readPos); @@ -49,7 +49,7 @@ Y_UNIT_TEST_SUITE(TDirectIoTestSuite) { for (size_t i = 0; i < readCount; ++i) { UNIT_ASSERT_VALUES_EQUAL(readResult[i], data[i]); } - NFs::Remove(FileName_); + NFs::Remove(FileName_); } void TestHugeFile(size_t size) { diff --git a/util/system/filemap.cpp b/util/system/filemap.cpp index f6e3a7f5b7..7454a4cb94 100644 --- a/util/system/filemap.cpp +++ b/util/system/filemap.cpp @@ -126,7 +126,7 @@ void NPrivate::Precharge(const void* data, size_t dataSize, size_t off, size_t s class TMemoryMap::TImpl: public TAtomicRefCount<TImpl> { public: - inline void CreateMapping() { + inline void CreateMapping() { #if defined(_win_) Mapping_ = nullptr; if (Length_) { @@ -134,7 +134,7 @@ public: (Mode_ & oAccessMask) == TFileMap::oRdWr ? PAGE_READWRITE : PAGE_READONLY, (DWORD)(Length_ >> 32), (DWORD)(Length_ & 0xFFFFFFFF), nullptr); if (Mapping_ == nullptr) { - ythrow yexception() << "Can't create file mapping of '" << DbgName_ << "': " << LastSystemErrorText(); + ythrow yexception() << "Can't create file mapping of '" << DbgName_ << "': " << LastSystemErrorText(); } } else { Mapping_ = MAP_FAILED; @@ -144,7 +144,7 @@ public: PtrStart_ = mmap((caddr_t) nullptr, Length_, ModeToMmapProt(Mode_), ModeToMmapFlags(Mode_), File_.GetHandle(), 0); if ((MAP_FAILED == PtrStart_) && Length_) { - ythrow yexception() << "Can't map " << (unsigned long)Length_ << " bytes of file '" << DbgName_ << "' at offset 0: " << LastSystemErrorText(); + ythrow yexception() << "Can't map " << (unsigned long)Length_ << " bytes of file '" << DbgName_ << "' at offset 0: " << LastSystemErrorText(); } } else { PtrStart_ = nullptr; @@ -152,58 +152,58 @@ public: #endif } - void CheckFile() const { + void CheckFile() const { if (!File_.IsOpen()) { - ythrow yexception() << "TMemoryMap: FILE '" << DbgName_ << "' is not open, " << LastSystemErrorText(); + ythrow yexception() << "TMemoryMap: FILE '" << DbgName_ << "' is not open, " << LastSystemErrorText(); } if (Length_ < 0) { - ythrow yexception() << "'" << DbgName_ << "' is not a regular file"; + ythrow yexception() << "'" << DbgName_ << "' is not a regular file"; } } inline TImpl(FILE* f, EOpenMode om, TString dbgName) : File_(Duplicate(f)) - , DbgName_(std::move(dbgName)) + , DbgName_(std::move(dbgName)) , Length_(File_.GetLength()) , Mode_(om) { - CheckFile(); - CreateMapping(); + CheckFile(); + CreateMapping(); } inline TImpl(const TString& name, EOpenMode om) : File_(name, (om & oRdWr) ? OpenExisting | RdWr : OpenExisting | RdOnly) - , DbgName_(name) + , DbgName_(name) , Length_(File_.GetLength()) , Mode_(om) { - CheckFile(); - CreateMapping(); + CheckFile(); + CreateMapping(); } inline TImpl(const TString& name, i64 length, EOpenMode om) : File_(name, (om & oRdWr) ? OpenExisting | RdWr : OpenExisting | RdOnly) - , DbgName_(name) + , DbgName_(name) , Length_(length) , Mode_(om) { - CheckFile(); + CheckFile(); if (File_.GetLength() < Length_) { File_.Resize(Length_); } - CreateMapping(); + CreateMapping(); } inline TImpl(const TFile& file, EOpenMode om, TString dbgName) : File_(file) - , DbgName_(File_.GetName() ? File_.GetName() : std::move(dbgName)) + , DbgName_(File_.GetName() ? File_.GetName() : std::move(dbgName)) , Length_(File_.GetLength()) , Mode_(om) { - CheckFile(); - CreateMapping(); + CheckFile(); + CreateMapping(); } inline bool IsOpen() const noexcept { @@ -218,15 +218,15 @@ public: return (Mode_ & oRdWr || Mode_ & oCopyOnWr); } - inline TMapResult Map(i64 offset, size_t size) { + inline TMapResult Map(i64 offset, size_t size) { assert(File_.IsOpen()); if (offset > Length_) { - ythrow yexception() << "Can't map something at offset " << offset << " of '" << DbgName_ << "' with length " << Length_; + ythrow yexception() << "Can't map something at offset " << offset << " of '" << DbgName_ << "' with length " << Length_; } if (offset + (i64)size > Length_) { - ythrow yexception() << "Can't map " << (unsigned long)size << " bytes at offset " << offset << " of '" << DbgName_ << "' with length " << Length_; + ythrow yexception() << "Can't map " << (unsigned long)size << " bytes at offset " << offset << " of '" << DbgName_ << "' with length " << Length_; } TMapResult result; @@ -258,7 +258,7 @@ public: if (result.Ptr != nullptr || size == 0) { // allow map of size 0 result.Size = size; } else { - ythrow yexception() << "Can't map " << (unsigned long)size << " bytes at offset " << offset << " of '" << DbgName_ << "': " << LastSystemErrorText(); + ythrow yexception() << "Can't map " << (unsigned long)size << " bytes at offset " << offset << " of '" << DbgName_ << "': " << LastSystemErrorText(); } NSan::Unpoison(result.Ptr, result.Size); if (Mode_ & oPrecharge) { @@ -326,13 +326,13 @@ public: } inline TString GetDbgName() const { - return DbgName_; - } - - inline EOpenMode GetMode() const noexcept { - return Mode_; - } - + return DbgName_; + } + + inline EOpenMode GetMode() const noexcept { + return Mode_; + } + private: TFile File_; TString DbgName_; // This string is never used to actually open a file, only in exceptions @@ -362,29 +362,29 @@ TMemoryMap::TMemoryMap(const TString& name, i64 length, EOpenMode om) } TMemoryMap::TMemoryMap(FILE* f, TString dbgName) - : Impl_(new TImpl(f, EOpenModeFlag::oRdOnly, std::move(dbgName))) + : Impl_(new TImpl(f, EOpenModeFlag::oRdOnly, std::move(dbgName))) { } TMemoryMap::TMemoryMap(FILE* f, EOpenMode om, TString dbgName) - : Impl_(new TImpl(f, om, std::move(dbgName))) + : Impl_(new TImpl(f, om, std::move(dbgName))) { } TMemoryMap::TMemoryMap(const TFile& file, TString dbgName) - : Impl_(new TImpl(file, EOpenModeFlag::oRdOnly, std::move(dbgName))) + : Impl_(new TImpl(file, EOpenModeFlag::oRdOnly, std::move(dbgName))) { } TMemoryMap::TMemoryMap(const TFile& file, EOpenMode om, TString dbgName) - : Impl_(new TImpl(file, om, std::move(dbgName))) + : Impl_(new TImpl(file, om, std::move(dbgName))) { } TMemoryMap::~TMemoryMap() = default; -TMemoryMap::TMapResult TMemoryMap::Map(i64 offset, size_t size) { - return Impl_->Map(offset, size); +TMemoryMap::TMapResult TMemoryMap::Map(i64 offset, size_t size) { + return Impl_->Map(offset, size); } bool TMemoryMap::Unmap(void* ptr, size_t size) { @@ -395,18 +395,18 @@ bool TMemoryMap::Unmap(TMapResult region) { return Unmap(region.Ptr, region.Size); } -void TMemoryMap::ResizeAndReset(i64 size) { - EOpenMode om = Impl_->GetMode(); - TFile file = GetFile(); - file.Resize(size); - Impl_.Reset(new TImpl(file, om, Impl_->GetDbgName())); -} - -TMemoryMap::TMapResult TMemoryMap::ResizeAndRemap(i64 offset, size_t size) { - ResizeAndReset(offset + (i64)size); - return Map(offset, size); -} - +void TMemoryMap::ResizeAndReset(i64 size) { + EOpenMode om = Impl_->GetMode(); + TFile file = GetFile(); + file.Resize(size); + Impl_.Reset(new TImpl(file, om, Impl_->GetDbgName())); +} + +TMemoryMap::TMapResult TMemoryMap::ResizeAndRemap(i64 offset, size_t size) { + ResizeAndReset(offset + (i64)size); + return Map(offset, size); +} + void TMemoryMap::SetSequential() { Impl_->SetSequential(); } @@ -460,12 +460,12 @@ TFileMap::TFileMap(const TString& name, i64 length, EOpenMode om) } TFileMap::TFileMap(FILE* f, EOpenMode om, TString dbgName) - : Map_(f, om, dbgName) + : Map_(f, om, dbgName) { } TFileMap::TFileMap(const TFile& file, EOpenMode om, TString dbgName) - : Map_(file, om, dbgName) + : Map_(file, om, dbgName) { } @@ -491,19 +491,19 @@ void TFileMap::Flush(void* ptr, size_t size, bool sync) { #endif } -TFileMap::TMapResult TFileMap::Map(i64 offset, size_t size) { +TFileMap::TMapResult TFileMap::Map(i64 offset, size_t size) { + Unmap(); + Region_ = Map_.Map(offset, size); + return Region_; +} + +TFileMap::TMapResult TFileMap::ResizeAndRemap(i64 offset, size_t size) { + // explicit Unmap() is required because in oNotGreedy mode the Map_ object doesn't own the mapped area Unmap(); - Region_ = Map_.Map(offset, size); - return Region_; + Region_ = Map_.ResizeAndRemap(offset, size); + return Region_; } -TFileMap::TMapResult TFileMap::ResizeAndRemap(i64 offset, size_t size) { - // explicit Unmap() is required because in oNotGreedy mode the Map_ object doesn't own the mapped area - Unmap(); - Region_ = Map_.ResizeAndRemap(offset, size); - return Region_; -} - void TFileMap::Unmap() { if (!Region_.IsMapped()) { return; @@ -518,7 +518,7 @@ void TFileMap::Unmap() { TFileMap::~TFileMap() { try { - // explicit Unmap() is required because in oNotGreedy mode the Map_ object doesn't own the mapped area + // explicit Unmap() is required because in oNotGreedy mode the Map_ object doesn't own the mapped area Unmap(); } catch (...) { // ¯\_(ツ)_/¯ diff --git a/util/system/filemap.h b/util/system/filemap.h index 31eac6de47..11be64bff4 100644 --- a/util/system/filemap.h +++ b/util/system/filemap.h @@ -80,12 +80,12 @@ public: ~TMemoryMap(); - TMapResult Map(i64 offset, size_t size); + TMapResult Map(i64 offset, size_t size); bool Unmap(TMapResult region); - void ResizeAndReset(i64 size); - TMapResult ResizeAndRemap(i64 offset, size_t size); - + void ResizeAndReset(i64 size); + TMapResult ResizeAndRemap(i64 offset, size_t size); + i64 Length() const noexcept; bool IsOpen() const noexcept; bool IsWritable() const noexcept; @@ -118,8 +118,8 @@ public: ~TFileMap(); - TMapResult Map(i64 offset, size_t size); - TMapResult ResizeAndRemap(i64 offset, size_t size); + TMapResult Map(i64 offset, size_t size); + TMapResult ResizeAndRemap(i64 offset, size_t size); void Unmap(); void Flush(void* ptr, size_t size) { diff --git a/util/system/filemap_ut.cpp b/util/system/filemap_ut.cpp index c796562f26..73f109dc88 100644 --- a/util/system/filemap_ut.cpp +++ b/util/system/filemap_ut.cpp @@ -6,11 +6,11 @@ #include "filemap.h" -#include <util/system/fs.h> - -#include <cstring> -#include <cstdio> - +#include <util/system/fs.h> + +#include <cstring> +#include <cstdio> + Y_UNIT_TEST_SUITE(TFileMapTest) { static const char* FileName_("./mappped_file"); @@ -32,9 +32,9 @@ Y_UNIT_TEST_SUITE(TFileMapTest) { } mappedFile.Flush(); - TFileMap::TMapResult mapResult = mappedFile.Map(2, 2); - UNIT_ASSERT(mapResult.MappedSize() == 2); - UNIT_ASSERT(mapResult.MappedData() == mappedFile.Ptr()); + TFileMap::TMapResult mapResult = mappedFile.Map(2, 2); + UNIT_ASSERT(mapResult.MappedSize() == 2); + UNIT_ASSERT(mapResult.MappedData() == mappedFile.Ptr()); UNIT_ASSERT(mappedFile.MappedSize() == 2); UNIT_ASSERT(static_cast<char*>(mappedFile.Ptr())[0] == 'd' && static_cast<char*>(mappedFile.Ptr())[1] == 'e'); @@ -48,7 +48,7 @@ Y_UNIT_TEST_SUITE(TFileMapTest) { UNIT_ASSERT(static_cast<char*>(mappedFile2.Ptr())[0] == data[0] + 1); fclose(f); } - NFs::Remove(FileName_); + NFs::Remove(FileName_); } Y_UNIT_TEST(TestFileMap) { @@ -60,26 +60,26 @@ Y_UNIT_TEST_SUITE(TFileMapTest) { } Y_UNIT_TEST(TestFileRemap) { - const char data1[] = "01234"; - const char data2[] = "abcdefg"; + const char data1[] = "01234"; + const char data2[] = "abcdefg"; const char data3[] = "COPY"; - const char dataFinal[] = "012abcdefg"; - const size_t data2Shift = 3; - - TFile file(FileName_, CreateAlways | WrOnly); - file.Write(static_cast<const void*>(data1), sizeof(data1)); - file.Close(); - - { - TFileMap mappedFile(FileName_, TMemoryMapCommon::oRdWr); - mappedFile.Map(0, mappedFile.Length()); + const char dataFinal[] = "012abcdefg"; + const size_t data2Shift = 3; + + TFile file(FileName_, CreateAlways | WrOnly); + file.Write(static_cast<const void*>(data1), sizeof(data1)); + file.Close(); + + { + TFileMap mappedFile(FileName_, TMemoryMapCommon::oRdWr); + mappedFile.Map(0, mappedFile.Length()); UNIT_ASSERT(mappedFile.MappedSize() == sizeof(data1) && mappedFile.Length() == sizeof(data1)); - - mappedFile.ResizeAndRemap(data2Shift, sizeof(data2)); - memcpy(mappedFile.Ptr(), data2, sizeof(data2)); - } - + + mappedFile.ResizeAndRemap(data2Shift, sizeof(data2)); + memcpy(mappedFile.Ptr(), data2, sizeof(data2)); + } + { TFileMap mappedFile(FileName_, TMemoryMapCommon::oCopyOnWr); mappedFile.Map(0, mappedFile.Length()); @@ -94,38 +94,38 @@ Y_UNIT_TEST_SUITE(TFileMapTest) { UNIT_ASSERT(data[3] == 'Y'); } - TFile resFile(FileName_, RdOnly); - UNIT_ASSERT(resFile.GetLength() == sizeof(dataFinal)); - char buf[sizeof(dataFinal)]; - resFile.Read(buf, sizeof(dataFinal)); - UNIT_ASSERT(0 == memcmp(buf, dataFinal, sizeof(dataFinal))); - resFile.Close(); - - NFs::Remove(FileName_); - } - + TFile resFile(FileName_, RdOnly); + UNIT_ASSERT(resFile.GetLength() == sizeof(dataFinal)); + char buf[sizeof(dataFinal)]; + resFile.Read(buf, sizeof(dataFinal)); + UNIT_ASSERT(0 == memcmp(buf, dataFinal, sizeof(dataFinal))); + resFile.Close(); + + NFs::Remove(FileName_); + } + Y_UNIT_TEST(TestFileMapDbgName) { - // This test checks that dbgName passed to the TFileMap constructor is saved inside the object and appears - // in subsequent error messages. - const char* const dbgName = "THIS_IS_A_TEST"; - FILE* f = fopen(FileName_, "w+"); - UNIT_ASSERT(f); - { - TFileMap mappedFile(f, TFileMap::oRdWr, dbgName); - bool gotException = false; - try { - // trying to map an empty file to force an exception and check the message - mappedFile.Map(0, 1000); - } catch (const yexception& e) { - gotException = true; - UNIT_ASSERT_STRING_CONTAINS(e.what(), dbgName); - } - UNIT_ASSERT(gotException); - } - fclose(f); - NFs::Remove(FileName_); - } - + // This test checks that dbgName passed to the TFileMap constructor is saved inside the object and appears + // in subsequent error messages. + const char* const dbgName = "THIS_IS_A_TEST"; + FILE* f = fopen(FileName_, "w+"); + UNIT_ASSERT(f); + { + TFileMap mappedFile(f, TFileMap::oRdWr, dbgName); + bool gotException = false; + try { + // trying to map an empty file to force an exception and check the message + mappedFile.Map(0, 1000); + } catch (const yexception& e) { + gotException = true; + UNIT_ASSERT_STRING_CONTAINS(e.what(), dbgName); + } + UNIT_ASSERT(gotException); + } + fclose(f); + NFs::Remove(FileName_); + } + #if defined(_asan_enabled_) || defined(_msan_enabled_) //setrlimit incompatible with asan runtime #elif defined(_cygwin_) @@ -194,7 +194,7 @@ Y_UNIT_TEST_SUITE(TFileMapTest) { } #endif maps.clear(); - NFs::Remove(FileName_); + NFs::Remove(FileName_); } catch (...) { // TODO: RAII'ize all this stuff #if defined(_unix_) @@ -204,7 +204,7 @@ Y_UNIT_TEST_SUITE(TFileMapTest) { throw TSystemError() << "Cannot restore rlimit for virtual memory"; } #endif - NFs::Remove(FileName_); + NFs::Remove(FileName_); throw; } @@ -265,7 +265,7 @@ Y_UNIT_TEST_SUITE(TFileMapTest) { } UNIT_ASSERT(caught); } - NFs::Remove(FileName_); + NFs::Remove(FileName_); } Y_UNIT_TEST(TestMappedArray) { @@ -315,7 +315,7 @@ Y_UNIT_TEST_SUITE(TFileMapTest) { TString text = exc.what(); // exception should contain failed file name UNIT_ASSERT(text.find(FileName_) != TString::npos); } - NFs::Remove(FileName_); + NFs::Remove(FileName_); } Y_UNIT_TEST(TestMemoryMapIsWritable) { @@ -330,7 +330,7 @@ Y_UNIT_TEST_SUITE(TFileMapTest) { TMemoryMap mappedMem(FileName_, TMemoryMap::oRdWr); UNIT_ASSERT(mappedMem.IsWritable()); } - NFs::Remove(FileName_); + NFs::Remove(FileName_); } Y_UNIT_TEST(TestFileMapIsWritable) { @@ -354,6 +354,6 @@ Y_UNIT_TEST_SUITE(TFileMapTest) { TFileMap fileMap(FileName_, TFileMap::oRdWr); UNIT_ASSERT(fileMap.IsWritable()); } - NFs::Remove(FileName_); + NFs::Remove(FileName_); } }; diff --git a/util/system/mincore.cpp b/util/system/mincore.cpp index 7dfb241c86..8cbae72586 100644 --- a/util/system/mincore.cpp +++ b/util/system/mincore.cpp @@ -1,35 +1,35 @@ -#include "align.h" -#include "compiler.h" -#include "info.h" -#include "mincore.h" - -#include <util/generic/yexception.h> - -#include <cstring> - -#if defined(_unix_) +#include "align.h" +#include "compiler.h" +#include "info.h" +#include "mincore.h" + +#include <util/generic/yexception.h> + +#include <cstring> + +#if defined(_unix_) #include <sys/unistd.h> #include <sys/mman.h> #if defined(_android_) #include <sys/syscall.h> #endif -#endif - -void InCoreMemory(const void* addr, size_t len, unsigned char* vec, size_t vecLen) { -#if defined(_linux_) - const size_t pageSize = NSystemInfo::GetPageSize(); - void* maddr = const_cast<void*>(AlignDown(addr, pageSize)); - len = AlignUp(len, pageSize); - if (vecLen * pageSize < len) { - ythrow yexception() << "vector argument for mincore is too small: " << vecLen * pageSize << " < " << len; - } - if (::mincore(maddr, len, vec)) { - ythrow yexception() << LastSystemErrorText(); - } -#else - // pessimistic assumption: nothing is in core - Y_UNUSED(addr); - Y_UNUSED(len); - ::memset(vec, 0, vecLen); -#endif -} +#endif + +void InCoreMemory(const void* addr, size_t len, unsigned char* vec, size_t vecLen) { +#if defined(_linux_) + const size_t pageSize = NSystemInfo::GetPageSize(); + void* maddr = const_cast<void*>(AlignDown(addr, pageSize)); + len = AlignUp(len, pageSize); + if (vecLen * pageSize < len) { + ythrow yexception() << "vector argument for mincore is too small: " << vecLen * pageSize << " < " << len; + } + if (::mincore(maddr, len, vec)) { + ythrow yexception() << LastSystemErrorText(); + } +#else + // pessimistic assumption: nothing is in core + Y_UNUSED(addr); + Y_UNUSED(len); + ::memset(vec, 0, vecLen); +#endif +} diff --git a/util/system/mincore.h b/util/system/mincore.h index 1fc3f7d69c..0f5d8b78c7 100644 --- a/util/system/mincore.h +++ b/util/system/mincore.h @@ -1,38 +1,38 @@ -#pragma once - -#include "defaults.h" -/** - * Fills a vector that indicates whether pages of the calling process's virtual memory are resident in RAM. Each byte - * in the vector contains the status of a single page. The page size can be obtained via the NSystemInfo::GetPageSize() - * function. Use the IsPageInCore function to interpret the page status byte. - * - * Can be overly pessimistic: - * - Assumes nothing is in RAM on platforms other than Linux - * - Recent Linux kernels (4.21 and some backports) may return zeroes if the process doesn't have writing permissions - * for the given file. See CVE-2019-5489. - * - * @param[in] addr starting address of the memory range to be examined - * @param[in] len length (bytes) of the memory range to be examined - * @param[out] vec vector of bytes to store statuses of memory pages - * @param[in] vecLen length (bytes) of the vec, should be large enough to hold the requested pages count - * @throws yexception if there was a system error or if the vecLen is too small - * - * @note this is only a snapshot, results may be stale by the time they're used - * @see man 2 mincore - */ -void InCoreMemory(const void* addr, size_t len, unsigned char* vec, size_t vecLen); - -/** - * Takes as an argument an element of the vector previously filled by InCoreMemory. - * - * @param[in] byte corresponding to the status of a single page - * - * @returns true if this page was resident in memory at the time out the InCoreMemory execution - */ -inline bool IsPageInCore(unsigned char s) { - /* From mincore(2): On return, the least significant bit of each byte will be set if the corresponding page is - * currently resident in memory, and be clear otherwise. (The settings of the other bits in each byte are - * undefined; these bits are reserved for possible later use.) - */ - return s & 1; -} +#pragma once + +#include "defaults.h" +/** + * Fills a vector that indicates whether pages of the calling process's virtual memory are resident in RAM. Each byte + * in the vector contains the status of a single page. The page size can be obtained via the NSystemInfo::GetPageSize() + * function. Use the IsPageInCore function to interpret the page status byte. + * + * Can be overly pessimistic: + * - Assumes nothing is in RAM on platforms other than Linux + * - Recent Linux kernels (4.21 and some backports) may return zeroes if the process doesn't have writing permissions + * for the given file. See CVE-2019-5489. + * + * @param[in] addr starting address of the memory range to be examined + * @param[in] len length (bytes) of the memory range to be examined + * @param[out] vec vector of bytes to store statuses of memory pages + * @param[in] vecLen length (bytes) of the vec, should be large enough to hold the requested pages count + * @throws yexception if there was a system error or if the vecLen is too small + * + * @note this is only a snapshot, results may be stale by the time they're used + * @see man 2 mincore + */ +void InCoreMemory(const void* addr, size_t len, unsigned char* vec, size_t vecLen); + +/** + * Takes as an argument an element of the vector previously filled by InCoreMemory. + * + * @param[in] byte corresponding to the status of a single page + * + * @returns true if this page was resident in memory at the time out the InCoreMemory execution + */ +inline bool IsPageInCore(unsigned char s) { + /* From mincore(2): On return, the least significant bit of each byte will be set if the corresponding page is + * currently resident in memory, and be clear otherwise. (The settings of the other bits in each byte are + * undefined; these bits are reserved for possible later use.) + */ + return s & 1; +} diff --git a/util/system/mincore_ut.cpp b/util/system/mincore_ut.cpp index 1a7afcc6a5..fc46cb1632 100644 --- a/util/system/mincore_ut.cpp +++ b/util/system/mincore_ut.cpp @@ -1,47 +1,47 @@ #include <library/cpp/testing/unittest/registar.h> - -#ifdef _unix_ + +#ifdef _unix_ #include <sys/resource.h> -#endif - -#include "filemap.h" -#include "info.h" -#include "mincore.h" -#include "mlock.h" -#include "tempfile.h" - -#include <util/generic/size_literals.h> -#include <util/system/fs.h> - -#include <cstring> -#include <cstdio> - -Y_UNIT_TEST_SUITE(MincoreSuite) { - static const char* FileName_("./mappped_file"); - - Y_UNIT_TEST(TestLockAndInCore) { - TVector<char> content(2_MB); - - TTempFile cleanup(FileName_); - TFile file(FileName_, CreateAlways | WrOnly); - file.Write(content.data(), content.size()); - file.Close(); - - TFileMap mappedFile(FileName_, TMemoryMapCommon::oRdWr); - mappedFile.Map(0, mappedFile.Length()); - UNIT_ASSERT_EQUAL(mappedFile.MappedSize(), content.size()); - UNIT_ASSERT_EQUAL(mappedFile.Length(), static_cast<i64>(content.size())); - - LockMemory(mappedFile.Ptr(), mappedFile.Length()); - - TVector<unsigned char> incore(mappedFile.Length() / NSystemInfo::GetPageSize()); - InCoreMemory(mappedFile.Ptr(), mappedFile.Length(), incore.data(), incore.size()); - - // compile and run on all platforms, but assume non-zero results only on Linux -#if defined(_linux_) - for (const auto& flag : incore) { - UNIT_ASSERT(IsPageInCore(flag)); - } -#endif - } -} +#endif + +#include "filemap.h" +#include "info.h" +#include "mincore.h" +#include "mlock.h" +#include "tempfile.h" + +#include <util/generic/size_literals.h> +#include <util/system/fs.h> + +#include <cstring> +#include <cstdio> + +Y_UNIT_TEST_SUITE(MincoreSuite) { + static const char* FileName_("./mappped_file"); + + Y_UNIT_TEST(TestLockAndInCore) { + TVector<char> content(2_MB); + + TTempFile cleanup(FileName_); + TFile file(FileName_, CreateAlways | WrOnly); + file.Write(content.data(), content.size()); + file.Close(); + + TFileMap mappedFile(FileName_, TMemoryMapCommon::oRdWr); + mappedFile.Map(0, mappedFile.Length()); + UNIT_ASSERT_EQUAL(mappedFile.MappedSize(), content.size()); + UNIT_ASSERT_EQUAL(mappedFile.Length(), static_cast<i64>(content.size())); + + LockMemory(mappedFile.Ptr(), mappedFile.Length()); + + TVector<unsigned char> incore(mappedFile.Length() / NSystemInfo::GetPageSize()); + InCoreMemory(mappedFile.Ptr(), mappedFile.Length(), incore.data(), incore.size()); + + // compile and run on all platforms, but assume non-zero results only on Linux +#if defined(_linux_) + for (const auto& flag : incore) { + UNIT_ASSERT(IsPageInCore(flag)); + } +#endif + } +} diff --git a/util/system/unaligned_mem.h b/util/system/unaligned_mem.h index abcdde9fd6..4b84686f2f 100644 --- a/util/system/unaligned_mem.h +++ b/util/system/unaligned_mem.h @@ -6,11 +6,11 @@ #include <string.h> #include <type_traits> -// The following code used to have smart tricks assuming that unaligned reads and writes are OK on x86. This assumption -// is wrong because compiler may emit alignment-sensitive x86 instructions e.g. movaps. See IGNIETFERRO-735. - +// The following code used to have smart tricks assuming that unaligned reads and writes are OK on x86. This assumption +// is wrong because compiler may emit alignment-sensitive x86 instructions e.g. movaps. See IGNIETFERRO-735. + template <class T> -inline T ReadUnaligned(const void* from) noexcept { +inline T ReadUnaligned(const void* from) noexcept { T ret; memcpy(&ret, from, sizeof(T)); return ret; diff --git a/util/system/unaligned_mem_ut.cpp b/util/system/unaligned_mem_ut.cpp index 3c575d4f69..9de3f3e931 100644 --- a/util/system/unaligned_mem_ut.cpp +++ b/util/system/unaligned_mem_ut.cpp @@ -3,94 +3,94 @@ #include <library/cpp/testing/benchmark/bench.h> #include <library/cpp/testing/unittest/registar.h> -#include <util/system/compiler.h> - -#ifdef Y_HAVE_INT128 -namespace { - struct TUInt128 { - bool operator==(const TUInt128& other) const { - return x == other.x; - } - - ui64 Low() const { - return (ui64)x; - } - - ui64 High() const { - return (ui64)(x >> 64); - } - - static TUInt128 Max() { +#include <util/system/compiler.h> + +#ifdef Y_HAVE_INT128 +namespace { + struct TUInt128 { + bool operator==(const TUInt128& other) const { + return x == other.x; + } + + ui64 Low() const { + return (ui64)x; + } + + ui64 High() const { + return (ui64)(x >> 64); + } + + static TUInt128 Max() { return {~(__uint128_t)0}; - } - - __uint128_t x; - }; -} -#endif - + } + + __uint128_t x; + }; +} +#endif + Y_UNIT_TEST_SUITE(UnalignedMem) { Y_UNIT_TEST(TestReadWrite) { - alignas(ui64) char buf[100]; + alignas(ui64) char buf[100]; WriteUnaligned<ui16>(buf + 1, (ui16)1); WriteUnaligned<ui32>(buf + 1 + 2, (ui32)2); WriteUnaligned<ui64>(buf + 1 + 2 + 4, (ui64)3); - UNIT_ASSERT_VALUES_EQUAL(ReadUnaligned<ui16>(buf + 1), 1); - UNIT_ASSERT_VALUES_EQUAL(ReadUnaligned<ui32>(buf + 1 + 2), 2); - UNIT_ASSERT_VALUES_EQUAL(ReadUnaligned<ui64>(buf + 1 + 2 + 4), 3); + UNIT_ASSERT_VALUES_EQUAL(ReadUnaligned<ui16>(buf + 1), 1); + UNIT_ASSERT_VALUES_EQUAL(ReadUnaligned<ui32>(buf + 1 + 2), 2); + UNIT_ASSERT_VALUES_EQUAL(ReadUnaligned<ui64>(buf + 1 + 2 + 4), 3); } - + Y_UNIT_TEST(TestReadWriteRuntime) { - // Unlike the test above, this test avoids compile-time execution by a smart compiler. - // It is required to catch the SIGSEGV in case compiler emits an alignment-sensitive instruction. - - alignas(ui64) static char buf[100] = {0}; // static is required for Clobber to work - + // Unlike the test above, this test avoids compile-time execution by a smart compiler. + // It is required to catch the SIGSEGV in case compiler emits an alignment-sensitive instruction. + + alignas(ui64) static char buf[100] = {0}; // static is required for Clobber to work + WriteUnaligned<ui16>(buf + 1, (ui16)1); WriteUnaligned<ui32>(buf + 1 + 2, (ui32)2); WriteUnaligned<ui64>(buf + 1 + 2 + 4, (ui64)3); - NBench::Clobber(); - - auto val1 = ReadUnaligned<ui16>(buf + 1); - auto val2 = ReadUnaligned<ui32>(buf + 1 + 2); - auto val3 = ReadUnaligned<ui64>(buf + 1 + 2 + 4); - - Y_DO_NOT_OPTIMIZE_AWAY(&val1); - Y_DO_NOT_OPTIMIZE_AWAY(&val2); - Y_DO_NOT_OPTIMIZE_AWAY(&val3); - Y_DO_NOT_OPTIMIZE_AWAY(val1); - Y_DO_NOT_OPTIMIZE_AWAY(val2); - Y_DO_NOT_OPTIMIZE_AWAY(val3); - - UNIT_ASSERT_VALUES_EQUAL(val1, 1); - UNIT_ASSERT_VALUES_EQUAL(val2, 2); - UNIT_ASSERT_VALUES_EQUAL(val3, 3); - } -#ifdef Y_HAVE_INT128 + NBench::Clobber(); + + auto val1 = ReadUnaligned<ui16>(buf + 1); + auto val2 = ReadUnaligned<ui32>(buf + 1 + 2); + auto val3 = ReadUnaligned<ui64>(buf + 1 + 2 + 4); + + Y_DO_NOT_OPTIMIZE_AWAY(&val1); + Y_DO_NOT_OPTIMIZE_AWAY(&val2); + Y_DO_NOT_OPTIMIZE_AWAY(&val3); + Y_DO_NOT_OPTIMIZE_AWAY(val1); + Y_DO_NOT_OPTIMIZE_AWAY(val2); + Y_DO_NOT_OPTIMIZE_AWAY(val3); + + UNIT_ASSERT_VALUES_EQUAL(val1, 1); + UNIT_ASSERT_VALUES_EQUAL(val2, 2); + UNIT_ASSERT_VALUES_EQUAL(val3, 3); + } +#ifdef Y_HAVE_INT128 Y_UNIT_TEST(TestReadWrite128) { - alignas(TUInt128) char buf[100] = {0}; - + alignas(TUInt128) char buf[100] = {0}; + WriteUnaligned<TUInt128>(buf + 1, TUInt128::Max()); - auto val = ReadUnaligned<TUInt128>(buf + 1); - UNIT_ASSERT(val == TUInt128::Max()); - } + auto val = ReadUnaligned<TUInt128>(buf + 1); + UNIT_ASSERT(val == TUInt128::Max()); + } Y_UNIT_TEST(TestReadWriteRuntime128) { - // Unlike the test above, this test avoids compile-time execution by a smart compiler. - // It is required to catch the SIGSEGV in case compiler emits an alignment-sensitive instruction. - - alignas(TUInt128) static char buf[100] = {0}; // static is required for Clobber to work - + // Unlike the test above, this test avoids compile-time execution by a smart compiler. + // It is required to catch the SIGSEGV in case compiler emits an alignment-sensitive instruction. + + alignas(TUInt128) static char buf[100] = {0}; // static is required for Clobber to work + WriteUnaligned<TUInt128>(buf + 1, TUInt128::Max()); - NBench::Clobber(); - - auto val = ReadUnaligned<TUInt128>(buf + 1); - Y_DO_NOT_OPTIMIZE_AWAY(&val); - Y_DO_NOT_OPTIMIZE_AWAY(val.Low()); - Y_DO_NOT_OPTIMIZE_AWAY(val.High()); - - UNIT_ASSERT(val == TUInt128::Max()); - } -#endif + NBench::Clobber(); + + auto val = ReadUnaligned<TUInt128>(buf + 1); + Y_DO_NOT_OPTIMIZE_AWAY(&val); + Y_DO_NOT_OPTIMIZE_AWAY(val.Low()); + Y_DO_NOT_OPTIMIZE_AWAY(val.High()); + + UNIT_ASSERT(val == TUInt128::Max()); + } +#endif } diff --git a/util/system/ut/ya.make b/util/system/ut/ya.make index 8187e2110b..127e7c261e 100644 --- a/util/system/ut/ya.make +++ b/util/system/ut/ya.make @@ -19,10 +19,10 @@ IF (OS_DARWIN) TIMEOUT(3600) ENDIF() -PEERDIR( +PEERDIR( library/cpp/testing/benchmark -) - +) + SRCS( system/align_ut.cpp system/atexit_ut.cpp @@ -53,7 +53,7 @@ SRCS( system/info_ut.cpp system/interrupt_signals_ut.cpp system/mem_info_ut.cpp - system/mincore_ut.cpp + system/mincore_ut.cpp system/mutex_ut.cpp system/nice_ut.cpp system/pipe_ut.cpp diff --git a/util/ya.make b/util/ya.make index f602a9f343..6ebe7e40cf 100644 --- a/util/ya.make +++ b/util/ya.make @@ -292,7 +292,7 @@ JOIN_SRCS( system/madvise.cpp system/maxlen.cpp system/mem_info.cpp - system/mincore.cpp + system/mincore.cpp system/mktemp.cpp system/mlock.cpp system/mutex.cpp |