diff options
author | innokentii <innokentii@yandex-team.com> | 2022-09-29 17:04:39 +0300 |
---|---|---|
committer | innokentii <innokentii@yandex-team.com> | 2022-09-29 17:04:39 +0300 |
commit | b9c33d2970daa903dfdd7cfd222c293593e83536 (patch) | |
tree | 8adc97a90bb850ba7e5c0b6dc48959709ed89dd0 /library/cpp/actors | |
parent | d562e7c54fcae2c816d67834310ecfec90e8a69d (diff) | |
download | ydb-b9c33d2970daa903dfdd7cfd222c293593e83536.tar.gz |
Extend TContiguousData interface
extend TContiguousData interface
Diffstat (limited to 'library/cpp/actors')
-rw-r--r-- | library/cpp/actors/util/contiguous_data.h | 315 | ||||
-rw-r--r-- | library/cpp/actors/util/contiguous_data_ut.cpp | 117 | ||||
-rw-r--r-- | library/cpp/actors/util/rope.h | 32 | ||||
-rw-r--r-- | library/cpp/actors/util/rope_ut.cpp | 11 | ||||
-rw-r--r-- | library/cpp/actors/util/shared_data_rope_backend.h | 2 |
5 files changed, 438 insertions, 39 deletions
diff --git a/library/cpp/actors/util/contiguous_data.h b/library/cpp/actors/util/contiguous_data.h index ae4c913c20..6b19e42753 100644 --- a/library/cpp/actors/util/contiguous_data.h +++ b/library/cpp/actors/util/contiguous_data.h @@ -59,6 +59,11 @@ public: , Size(data.size()) {} + const char& operator[](size_t index) const { + Y_VERIFY_DEBUG(index < Size); + return Data[index]; + } + const char *data() const noexcept { return Data; } @@ -81,6 +86,15 @@ public: return TContiguousSpan(data() + pos, n); } + template<std::size_t Index> + auto get() const noexcept + { + static_assert(Index < 2, + "Index out of bounds for TContiguousSpan"); + if constexpr (Index == 0) return Data; + if constexpr (Index == 1) return Size; + } + friend bool operator==(const TContiguousSpan& x, const TContiguousSpan& y) { return Compare(x, y) == 0; } friend bool operator!=(const TContiguousSpan& x, const TContiguousSpan& y) { return Compare(x, y) != 0; } friend bool operator< (const TContiguousSpan& x, const TContiguousSpan& y) { return Compare(x, y) < 0; } @@ -90,13 +104,32 @@ public: private: static int Compare(const TContiguousSpan& x, const TContiguousSpan& y) { - if(int res = std::memcmp(x.data(), y.data(), std::min(x.size(), y.size())); res) { + if (int res = std::memcmp(x.data(), y.data(), std::min(x.size(), y.size())); res) { return res; } return x.size() - y.size(); } }; +namespace std +{ + template<> + struct tuple_size<::TContiguousSpan> + : integral_constant<size_t, 2> {}; + + template<> + struct tuple_element<0, ::TContiguousSpan> + { + using type = const char *; + }; + + template<> + struct tuple_element<1, ::TContiguousSpan> + { + using type = size_t; + }; +} + template < class TLeft, class TRight, @@ -241,7 +274,7 @@ struct IContiguousChunk : TThrRefBase { return GetDataMut(); } - virtual size_t GetCapacity() const = 0; + virtual size_t GetOccupiedMemorySize() const = 0; }; class TRope; @@ -254,6 +287,7 @@ class TContiguousData { enum class EType : uintptr_t { STRING, SHARED_DATA, + SHARED_DATA_OWNED, ROPE_CHUNK_BACKEND, }; @@ -274,10 +308,14 @@ class TContiguousData { TBackendHolder Owner = TBackend::Empty; // lower bits contain type of the owner public: + + static constexpr struct TOwnerToken {} OwnerToken; + static constexpr size_t CookiesSize = 2 * sizeof(void*); + TBackend() = default; TBackend(const TBackend& other) - : Owner(Clone(other.Owner)) + : Owner(other.Owner ? Clone(other.Owner) : TBackend::Empty) {} TBackend(TBackend&& other) @@ -288,6 +326,10 @@ class TContiguousData { : Owner(Construct<TString>(EType::STRING, std::move(s))) {} + TBackend(NActors::TSharedData s, TOwnerToken) + : Owner(Construct<NActors::TSharedData>(EType::SHARED_DATA_OWNED, std::move(s))) + {} + TBackend(NActors::TSharedData s) : Owner(Construct<NActors::TSharedData>(EType::SHARED_DATA, std::move(s))) {} @@ -310,7 +352,11 @@ class TContiguousData { if (Owner) { Destroy(Owner); } - Owner = Clone(other.Owner); + if (other.Owner) { + Owner = Clone(other.Owner); + } else { + Owner = TBackend::Empty; + } return *this; } @@ -335,12 +381,19 @@ class TContiguousData { } TContiguousSpan GetData() const { - return Visit(Owner, [](EType, auto& value) -> TContiguousSpan { + if (!Owner) { + return TContiguousSpan(); + } + return Visit(Owner, [](EType type, auto& value) -> TContiguousSpan { using T = std::decay_t<decltype(value)>; if constexpr (std::is_same_v<T, TString>) { - return {value.data(), value.size()}; + return {&(*value.cbegin()), value.size()}; } else if constexpr (std::is_same_v<T, NActors::TSharedData>) { - return {value.data(), value.size()}; + if (type == EType::SHARED_DATA_OWNED) { + return {value.data(), value.size() - CookiesSize}; + } else { + return {value.data(), value.size()}; + } } else if constexpr (std::is_same_v<T, IContiguousChunk::TPtr>) { return value->GetData(); } else { @@ -350,15 +403,22 @@ class TContiguousData { } TMutableContiguousSpan GetDataMut() { - return Visit(Owner, [](EType, auto& value) -> TMutableContiguousSpan { + if (!Owner) { + return TMutableContiguousSpan(); + } + return Visit(Owner, [](EType type, auto& value) -> TMutableContiguousSpan { using T = std::decay_t<decltype(value)>; if constexpr (std::is_same_v<T, TString>) { return {value.Detach(), value.size()}; } else if constexpr (std::is_same_v<T, NActors::TSharedData>) { - if(value.IsShared()) { + if (value.IsShared()) { value = NActors::TSharedData::Copy(value.data(), value.size()); } - return {value.mutable_data(), value.size()}; + if (type == EType::SHARED_DATA_OWNED) { + return {value.mutable_data(), value.size() - CookiesSize}; + } else { + return {value.mutable_data(), value.size()}; + } } else if constexpr (std::is_same_v<T, IContiguousChunk::TPtr>) { return value->GetDataMut(); } else { @@ -368,12 +428,19 @@ class TContiguousData { } TMutableContiguousSpan UnsafeGetDataMut() const { - return Visit(Owner, [](EType, auto& value) -> TMutableContiguousSpan { + if (!Owner) { + return TMutableContiguousSpan(); + } + return Visit(Owner, [](EType type, auto& value) -> TMutableContiguousSpan { using T = std::decay_t<decltype(value)>; if constexpr (std::is_same_v<T, TString>) { return {const_cast<char*>(value.data()), value.size()}; } else if constexpr (std::is_same_v<T, NActors::TSharedData>) { - return {const_cast<char*>(value.data()), value.size()}; + if (type == EType::SHARED_DATA_OWNED) { + return {const_cast<char*>(value.data()), value.size() - CookiesSize}; + } else { + return {const_cast<char*>(value.data()), value.size()}; + } } else if constexpr (std::is_same_v<T, IContiguousChunk::TPtr>) { return value->UnsafeGetDataMut(); } else { @@ -382,7 +449,10 @@ class TContiguousData { }); } - size_t GetCapacity() const { + size_t GetOccupiedMemorySize() const { + if (!Owner) { + return 0; + } return Visit(Owner, [](EType, auto& value) { using T = std::decay_t<decltype(value)>; if constexpr (std::is_same_v<T, TString>) { @@ -390,7 +460,7 @@ class TContiguousData { } else if constexpr (std::is_same_v<T, NActors::TSharedData>) { return value.size(); // There is no capacity } else if constexpr (std::is_same_v<T, IContiguousChunk::TPtr>) { - return value->GetCapacity(); + return value->GetOccupiedMemorySize(); } else { Y_FAIL(); } @@ -399,17 +469,73 @@ class TContiguousData { template <class TType> bool ContainsNativeType() const { + if (!Owner) { + return false; + } return Visit(Owner, [](EType, auto& value) { using T = std::decay_t<decltype(value)>; return std::is_same_v<T, TType>; }); } + bool OwnsContainer(const char* Begin, const char* End) const { + if (!Owner) { + return false; + } + return Visit(Owner, [Begin, End](EType type, auto& value) { + using T = std::decay_t<decltype(value)>; + if constexpr (std::is_same_v<T, NActors::TSharedData>) { + if (type == EType::SHARED_DATA_OWNED) { + auto* begin = value.data() + value.size() - CookiesSize; + return std::memcmp(begin, (char*)&(Begin), sizeof(char*)) == 0 + && std::memcmp(begin + sizeof(char*), (char*)&(End), sizeof(char*)) == 0; + // TODO(innokentii) value.IsPrivate(); Think about this case - it could mean that it is ok to overwrite + } + } + return false; + }); + } + + void UpdateCookies(const char* Begin, const char* End) { + if (!Owner) { + return; + } + return Visit(Owner, [Begin, End](EType type, auto& value) { + using T = std::decay_t<decltype(value)>; + if constexpr (std::is_same_v<T, NActors::TSharedData>) { + if (type == EType::SHARED_DATA_OWNED) { + auto* begin = const_cast<char*>(value.data() + value.size() - CookiesSize); + std::memcpy(begin, (char*)&(Begin), sizeof(char*)); + std::memcpy(begin + sizeof(char*), (char*)&(End), sizeof(char*)); + } + } + }); + } + + void Disown() { + if (Owner) { + const EType type = static_cast<EType>(Owner.Data[0] & TypeMask); + if (type == EType::SHARED_DATA_OWNED) { + Owner.Data[0] = (Owner.Data[0] & ValueMask) | static_cast<uintptr_t>(EType::SHARED_DATA); + } + } + } + template <class TResult> TResult GetRaw() const { - return Visit(Owner, [](EType, auto& value) { + if (!Owner) { + return TResult{}; + } + return Visit(Owner, [](EType type, auto& value) { using T = std::decay_t<decltype(value)>; if constexpr (std::is_same_v<T, TResult>) { + if constexpr (std::is_same_v<T, NActors::TSharedData>) { + if (type == EType::SHARED_DATA_OWNED) { + NActors::TSharedData data = value; + data.Trim(data.size() - CookiesSize); + return data; + } + } return value; } else { Y_FAIL(); @@ -482,6 +608,7 @@ class TContiguousData { }; switch (type) { case EType::STRING: return wrapper(reinterpret_cast<std::conditional_t<IsConst, const TString&, TString&>>(value)); + case EType::SHARED_DATA_OWNED: [[fallthrough]]; case EType::SHARED_DATA: return wrapper(reinterpret_cast<std::conditional_t<IsConst, const NActors::TSharedData&, NActors::TSharedData&>>(value)); case EType::ROPE_CHUNK_BACKEND: return wrapper(reinterpret_cast<std::conditional_t<IsConst, const IContiguousChunk::TPtr&, IContiguousChunk::TPtr&>>(value)); } @@ -515,13 +642,44 @@ class TContiguousData { } }; + static constexpr struct TOwnedSlice {} OwnedSlice{}; + TBackend Backend; // who actually holds the data const char *Begin; // data start const char *End; // data end + explicit TContiguousData(NActors::TSharedData s, TBackend::TOwnerToken) + : Backend(std::move(s), TBackend::OwnerToken) + { + auto span = Backend.GetData(); + Begin = span.data(); + End = Begin + span.size(); + std::memcpy(const_cast<char*>(End), (&Begin), sizeof(Begin)); + std::memcpy(const_cast<char*>(End) + sizeof(Begin), (&End), sizeof(End)); + } + + TContiguousData(TOwnedSlice, const char *data, size_t size, const TContiguousData& from) + : TContiguousData(from.Backend, {data, size}) + { + Y_VERIFY(data >= from.GetData()); + Y_VERIFY(data < from.GetData() + from.GetSize()); + Y_VERIFY(data + size <= from.GetData() + from.GetSize()); + Backend.UpdateCookies(Begin, End); + } + + TContiguousData(TOwnedSlice, const char *begin, const char *end, const TContiguousData& from) + : TContiguousData(OwnedSlice, begin, end - begin, from) + {} + public: static constexpr struct TSlice {} Slice{}; + enum class EResizeStrategy { + KeepRooms, + FailOnCopy, + // SaveAllocs, // Move data if there is enough space in (headroom + size + tailroom) + }; + TContiguousData() : Begin(nullptr) , End(nullptr) @@ -532,9 +690,7 @@ public: : Backend(std::forward<T>(backend)) , Begin(data.data()) , End(Begin + data.size()) - { - Y_VERIFY_DEBUG(Begin != End); - } + {} explicit TContiguousData(TString s) : Backend(std::move(s)) @@ -558,7 +714,12 @@ public: TContiguousData(TSlice, const char *data, size_t size, const TContiguousData& from) : TContiguousData(from.Backend, {data, size}) - {} + { + Y_VERIFY(data >= from.GetData()); + Y_VERIFY(data < from.GetData() + from.GetSize()); + Y_VERIFY(data + size <= from.GetData() + from.GetSize()); + Backend.Disown(); + } TContiguousData(TSlice, const char *begin, const char *end, const TContiguousData& from) : TContiguousData(Slice, begin, end - begin, from) @@ -579,6 +740,28 @@ public: TContiguousData& operator =(const TContiguousData&) = default; TContiguousData& operator =(TContiguousData&&) = default; + static TContiguousData Uninitialized(size_t size, size_t headroom = 0, size_t tailroom = 0) + { + if (size == 0) { + return TContiguousData(); + } + if (headroom == 0 && tailroom == 0) { + NActors::TSharedData res = NActors::TSharedData::Uninitialized(size + headroom + tailroom); + return TContiguousData( + OwnedSlice, + res.data() + headroom, + res.data() + res.size() - tailroom, + TContiguousData(res)); + } else { + NActors::TSharedData res = NActors::TSharedData::Uninitialized(size + headroom + tailroom + TBackend::CookiesSize); + return TContiguousData( + OwnedSlice, + res.data() + headroom, + res.data() + res.size() - tailroom - TBackend::CookiesSize, + TContiguousData(res, TBackend::OwnerToken)); + } + } + template <class TType> bool ContainsNativeType() const { return Backend.ContainsNativeType<TType>(); @@ -593,12 +776,20 @@ public: return Backend.GetData().size() == GetSize(); } + bool OwnsContainer() const noexcept { + return Backend.OwnsContainer(Begin, End); + } + size_t GetSize() const { return End - Begin; } - size_t GetCapacity() const { - return Backend.GetCapacity(); + size_t Size() const { + return End - Begin; + } + + size_t GetOccupiedMemorySize() const { + return Backend.GetOccupiedMemorySize(); } const char* GetData() const { @@ -649,18 +840,92 @@ public: return {UnsafeGetDataMut(), GetSize()}; } - size_t size() const { - return GetSize(); + bool HasBuffer() const { + return static_cast<bool>(Backend); } - size_t capacity() const { - return GetCapacity(); + size_t size() const { + return GetSize(); } const char* data() const { return GetData(); } + const char* begin() const { + return Begin; + } + + const char* end() const { + return End; + } + + void GrowFront(size_t size, EResizeStrategy strategy = EResizeStrategy::KeepRooms) { + Y_UNUSED(strategy); + if (Headroom() >= size) { + Begin -= size; + Backend.UpdateCookies(Begin, End); + } else { + if (strategy == EResizeStrategy::FailOnCopy && static_cast<bool>(Backend)) { + Y_FAIL("Fail on grow"); + } + auto newData = TContiguousData::Uninitialized(size + GetSize(), UnsafeHeadroom() > size ? UnsafeHeadroom() - size : 0, UnsafeTailroom()); + if (auto data = GetData(); data) { + std::memcpy(newData.UnsafeGetDataMut() + size, GetData(), GetSize()); + } + *this = std::move(newData); + } + } + + void GrowBack(size_t size, EResizeStrategy strategy = EResizeStrategy::KeepRooms) { + Y_UNUSED(strategy); + if (Tailroom() > size) { + End += size; + Backend.UpdateCookies(Begin, End); + } else { + if (strategy == EResizeStrategy::FailOnCopy && static_cast<bool>(Backend)) { + Y_FAIL("Fail on grow"); + } + auto newData = TContiguousData::Uninitialized(size + GetSize(), UnsafeHeadroom(), UnsafeTailroom() > size ? UnsafeTailroom() - size : 0); + if (auto data = GetData(); data) { + std::memcpy(newData.UnsafeGetDataMut(), GetData(), GetSize()); + } + *this = std::move(newData); + } + } + + void Trim(size_t size, size_t frontOffset = 0) { + Y_VERIFY(size <= End - Begin - frontOffset); + Backend.Disown(); + Begin = Begin + frontOffset; + End = Begin + size; + } + + size_t UnsafeHeadroom() const { + return Begin - Backend.GetData().data(); + } + + size_t UnsafeTailroom() const { + auto [data, size] = Backend.GetData(); + return (data + size) - End; + } + + size_t Headroom() const { + if (Backend.OwnsContainer(Begin, End)) { + return UnsafeHeadroom(); + } + + return 0; + } + + size_t Tailroom() const { + if (Backend.OwnsContainer(Begin, End)) { + return UnsafeTailroom(); + } + + return 0; + } + operator TContiguousSpan() const noexcept { return TContiguousSpan(GetData(), GetSize()); } diff --git a/library/cpp/actors/util/contiguous_data_ut.cpp b/library/cpp/actors/util/contiguous_data_ut.cpp index b9361b3f39..a792015ce3 100644 --- a/library/cpp/actors/util/contiguous_data_ut.cpp +++ b/library/cpp/actors/util/contiguous_data_ut.cpp @@ -49,4 +49,121 @@ Y_UNIT_TEST_SUITE(TContiguousData) { sharedData, constSharedData); } + + Y_UNIT_TEST(Resize) { + TContiguousData data = TContiguousData::Uninitialized(10, 20, 30); + UNIT_ASSERT_EQUAL(data.size(), 10); + UNIT_ASSERT_EQUAL(data.Headroom(), 20); + UNIT_ASSERT_EQUAL(data.Tailroom(), 30); + UNIT_ASSERT_EQUAL(data.GetOccupiedMemorySize(), 76); + UNIT_ASSERT_EQUAL(data.GetOccupiedMemorySize(), data.size() + data.Headroom() + data.Tailroom() + 2 * sizeof(char*)); + data.GrowFront(5); + UNIT_ASSERT_EQUAL(data.size(), 15); + UNIT_ASSERT_EQUAL(data.Headroom(), 15); + UNIT_ASSERT_EQUAL(data.Tailroom(), 30); + UNIT_ASSERT_EQUAL(data.GetOccupiedMemorySize(), 76); + UNIT_ASSERT_EQUAL(data.GetOccupiedMemorySize(), data.size() + data.Headroom() + data.Tailroom() + 2 * sizeof(char*)); + data.GrowBack(5); + UNIT_ASSERT_EQUAL(data.size(), 20); + UNIT_ASSERT_EQUAL(data.Headroom(), 15); + UNIT_ASSERT_EQUAL(data.Tailroom(), 25); + UNIT_ASSERT_EQUAL(data.GetOccupiedMemorySize(), 76); + UNIT_ASSERT_EQUAL(data.GetOccupiedMemorySize(), data.size() + data.Headroom() + data.Tailroom() + 2 * sizeof(char*)); + data.GrowFront(21); + UNIT_ASSERT_EQUAL(data.size(), 41); + UNIT_ASSERT_EQUAL(data.Headroom(), 0); + UNIT_ASSERT_EQUAL(data.Tailroom(), 25); + UNIT_ASSERT_EQUAL(data.GetOccupiedMemorySize(), 82); + UNIT_ASSERT_EQUAL(data.GetOccupiedMemorySize(), data.size() + data.Headroom() + data.Tailroom() + 2 * sizeof(char*)); + data.GrowBack(26); + UNIT_ASSERT_EQUAL(data.size(), 67); + UNIT_ASSERT_EQUAL(data.Headroom(), 0); + UNIT_ASSERT_EQUAL(data.Tailroom(), 0); + UNIT_ASSERT_EQUAL(data.GetOccupiedMemorySize(), 67); + UNIT_ASSERT_EQUAL(data.GetOccupiedMemorySize(), data.size() + data.Headroom() + data.Tailroom()); + } + + Y_UNIT_TEST(ResizeUnshare) { + TContiguousData data = TContiguousData::Uninitialized(10, 20, 30); + TContiguousData otherData(data); + UNIT_ASSERT_EQUAL(data.data(), otherData.data()); + UNIT_ASSERT_EQUAL(data.size(), 10); + UNIT_ASSERT_EQUAL(data.Headroom(), 20); + UNIT_ASSERT_EQUAL(data.Tailroom(), 30); + UNIT_ASSERT_EQUAL(data.GetOccupiedMemorySize(), 76); + UNIT_ASSERT_EQUAL(otherData.size(), 10); + UNIT_ASSERT_EQUAL(otherData.Headroom(), 20); + UNIT_ASSERT_EQUAL(otherData.Tailroom(), 30); + UNIT_ASSERT_EQUAL(otherData.GetOccupiedMemorySize(), 76); + data.GrowFront(5); + data.GrowBack(5); + UNIT_ASSERT_EQUAL(data.data() + 5, otherData.data()); + UNIT_ASSERT_EQUAL(data.size(), 20); + UNIT_ASSERT_EQUAL(data.Headroom(), 15); + UNIT_ASSERT_EQUAL(data.Tailroom(), 25); + UNIT_ASSERT_EQUAL(data.GetOccupiedMemorySize(), 76); + otherData.GrowFront(5); + UNIT_ASSERT_UNEQUAL(data.data(), otherData.data()); + UNIT_ASSERT_EQUAL(otherData.size(), 15); + UNIT_ASSERT_EQUAL(otherData.Headroom(), 15); + UNIT_ASSERT_EQUAL(otherData.Tailroom(), 30); + UNIT_ASSERT_EQUAL(otherData.GetOccupiedMemorySize(), 76); + } + + Y_UNIT_TEST(Trim) { + TContiguousData data = TContiguousData::Uninitialized(10, 20, 30); + TContiguousData otherData(data); + otherData.Trim(5); + UNIT_ASSERT_EQUAL(data.data(), otherData.data()); + UNIT_ASSERT_EQUAL(otherData.Headroom(), 0); + UNIT_ASSERT_EQUAL(otherData.Tailroom(), 0); + TContiguousData otherData2(data); + otherData2.Trim(1, 1); + UNIT_ASSERT_EQUAL(data.data() + 1, otherData2.data()); + UNIT_ASSERT_EQUAL(otherData2.Headroom(), 0); + UNIT_ASSERT_EQUAL(otherData2.Tailroom(), 0); + otherData.Trim(1, 1); + UNIT_ASSERT_EQUAL(otherData.data(), otherData2.data()); + data.GrowFront(5); + data.GrowBack(5); + UNIT_ASSERT_EQUAL(data.data() + 6, otherData2.data()); + UNIT_ASSERT_EQUAL(data.data() + 6, otherData.data()); + otherData.GrowFront(1); + UNIT_ASSERT_UNEQUAL(data.data() + 7, otherData.data()); + otherData2.GrowBack(1); + UNIT_ASSERT_UNEQUAL(data.data() + 6, otherData2.data()); + data = TContiguousData::Uninitialized(10); + otherData = data; + data.Trim(5); + UNIT_ASSERT_EQUAL(data.data(), otherData.data()); + UNIT_ASSERT_EQUAL(data.size(), 5); + } + + Y_UNIT_TEST(SliceUnshare) { + TContiguousData data = TContiguousData::Uninitialized(10, 20, 30); + TContiguousData otherData(TContiguousData::Slice, data.data(), data.size(), data); + UNIT_ASSERT_EQUAL(otherData.Headroom(), 0); + UNIT_ASSERT_EQUAL(otherData.Tailroom(), 0); + } + + Y_UNIT_TEST(Extract) { + TContiguousData data = TContiguousData::Uninitialized(10, 20, 30); + NActors::TSharedData extracted = data.ExtractUnderlyingContainerOrCopy<NActors::TSharedData>(); + UNIT_ASSERT_UNEQUAL(data.data(), extracted.data()); + UNIT_ASSERT_UNEQUAL(data.data(), extracted.data() + 20); + data = TContiguousData::Uninitialized(10); + extracted = data.ExtractUnderlyingContainerOrCopy<NActors::TSharedData>(); + UNIT_ASSERT_EQUAL(data.data(), extracted.data()); + + // TODO(innokentii) add microoptimization for this case + // TContiguousData data = TContiguousData::Uninitialized(10, 20, 30); + // data.GrowFront(20); + // extracted = data.ExtractUnderlyingContainerOrCopy<NActors::TSharedData>(); + // UNIT_ASSERT_EQUAL(data.data(), extracted.data()); + // UNIT_ASSERT_EQUAL(data.size(), extracted.size()); + // data.GrowBack(30); + // extracted = data.ExtractUnderlyingContainerOrCopy<NActors::TSharedData>(); + // UNIT_ASSERT_EQUAL(data.data(), extracted.data()); + // UNIT_ASSERT_EQUAL(data.size(), extracted.size()); + } } diff --git a/library/cpp/actors/util/rope.h b/library/cpp/actors/util/rope.h index 2a2e25f3a2..e713cd241a 100644 --- a/library/cpp/actors/util/rope.h +++ b/library/cpp/actors/util/rope.h @@ -62,7 +62,11 @@ public: return {Data + Offset, Size}; } - size_t GetCapacity() const override { + size_t GetOccupiedMemorySize() const override { + return Capacity; + } + + size_t GetCapacity() const { return Capacity; } @@ -356,10 +360,18 @@ public: TRope(const TRope& rope) = default; TRope(const TContiguousData& data) { + if(!data.HasBuffer()) { + return; + } + Size = data.GetSize(); Chain.PutToEnd(data); } TRope(TContiguousData&& data) { + if(!data.HasBuffer()) { + return; + } + Size = data.GetSize(); Chain.PutToEnd(std::move(data)); } @@ -727,8 +739,8 @@ public: void Compact() { if(!IsContiguous()) { // TODO(innokentii): use better container, when most outer users stop use TString - TString res = TString::Uninitialized(GetSize()); - Begin().ExtractPlainDataAndAdvance(res.Detach(), res.size()); + TContiguousData res = TContiguousData::Uninitialized(GetSize()); + Begin().ExtractPlainDataAndAdvance(res.UnsafeGetDataMut(), res.size()); Erase(Begin(), End()); Insert(End(), TRope(res)); } @@ -736,7 +748,7 @@ public: static TRope Uninitialized(size_t size) { - TString res = TString::Uninitialized(size); + TContiguousData res = TContiguousData::Uninitialized(size); return TRope(res); } @@ -790,6 +802,9 @@ public: } explicit operator TContiguousData() { + if(GetSize() == 0) { + return TContiguousData(); + } Compact(); return TContiguousData(Begin().GetChunk()); } @@ -853,11 +868,12 @@ private: // consider special case -- when begin and end point to the same block; in this case we have to split up this // block into two parts if (begin.Iter == end.Iter) { - addBlock(begin.GetChunk(), begin.Ptr, end.Ptr); + TContiguousData chunkToSplit = begin.GetChunk(); + addBlock(chunkToSplit, begin.Ptr, end.Ptr); const char *firstChunkBegin = begin.PointsToChunkMiddle() ? begin->Begin : nullptr; begin->Begin = end.Ptr; // this affects both begin and end iterator pointed values if (firstChunkBegin) { - Chain.InsertBefore(begin.Iter, TContiguousData::Slice, firstChunkBegin, begin.Ptr, begin.GetChunk()); + Chain.InsertBefore(begin.Iter, TContiguousData::Slice, firstChunkBegin, begin.Ptr, chunkToSplit); } } else { // check the first iterator -- if it starts not from the begin of the block, we have to adjust end of the @@ -941,7 +957,7 @@ public: void AccountChunk(const TContiguousData& chunk) { if (AccountedBuffers.insert(chunk.Backend.UniqueId()).second) { - Size += chunk.GetCapacity(); + Size += chunk.GetOccupiedMemorySize(); } } }; @@ -1073,7 +1089,7 @@ public: inline TRope TRope::CopySpaceOptimized(TRope&& origin, size_t worstRatioPer1k, TRopeArena& arena) { TRope res; for (TContiguousData& chunk : origin.Chain) { - size_t ratio = chunk.GetSize() * 1024 / chunk.GetCapacity(); + size_t ratio = chunk.GetSize() * 1024 / chunk.GetOccupiedMemorySize(); if (ratio < 1024 - worstRatioPer1k) { res.Insert(res.End(), arena.CreateRope(chunk.Begin, chunk.GetSize())); } else { diff --git a/library/cpp/actors/util/rope_ut.cpp b/library/cpp/actors/util/rope_ut.cpp index de7dd2c9ea..5ca02d8043 100644 --- a/library/cpp/actors/util/rope_ut.cpp +++ b/library/cpp/actors/util/rope_ut.cpp @@ -23,7 +23,7 @@ public: return {const_cast<char*>(Buffer.data()), Buffer.size()}; } - size_t GetCapacity() const override { + size_t GetOccupiedMemorySize() const override { return Buffer.capacity(); } }; @@ -91,7 +91,7 @@ Y_UNIT_TEST_SUITE(TRope) { Y_UNIT_TEST(Compacted) { TRope rope = CreateRope(Text, 10); - UNIT_ASSERT_EQUAL(rope.UnsafeGetContiguousSpanMut().data(), Text); + UNIT_ASSERT_EQUAL(rope.UnsafeGetContiguousSpanMut(), Text); UNIT_ASSERT(rope.IsContiguous()); } @@ -141,12 +141,13 @@ Y_UNIT_TEST_SUITE(TRope) { Y_UNIT_TEST(ContiguousDataInterop) { TString string = "Some long-long text needed for not sharing data and testing"; TContiguousData data(string); + UNIT_ASSERT_EQUAL(data.UnsafeGetDataMut(), &(*string.cbegin())); TRope rope(data); // check operator TRope - UNIT_ASSERT_EQUAL(rope.UnsafeGetContiguousSpanMut().data(), string.data()); + UNIT_ASSERT_EQUAL(rope.UnsafeGetContiguousSpanMut().data(), &(*string.cbegin())); TContiguousData otherData(rope); - UNIT_ASSERT_EQUAL(otherData.UnsafeGetDataMut(), string.data()); + UNIT_ASSERT_EQUAL(otherData.UnsafeGetDataMut(), &(*string.cbegin())); TString extractedBack = otherData.ExtractUnderlyingContainerOrCopy<TString>(); - UNIT_ASSERT_EQUAL(extractedBack.data(), string.data()); + UNIT_ASSERT_EQUAL(extractedBack.data(), &(*string.cbegin())); } #endif Y_UNIT_TEST(CrossCompare) { diff --git a/library/cpp/actors/util/shared_data_rope_backend.h b/library/cpp/actors/util/shared_data_rope_backend.h index 312dab9112..f6f2b2a49a 100644 --- a/library/cpp/actors/util/shared_data_rope_backend.h +++ b/library/cpp/actors/util/shared_data_rope_backend.h @@ -29,7 +29,7 @@ public: return {const_cast<char *>(Buffer.data()), Buffer.size()}; } - size_t GetCapacity() const override { + size_t GetOccupiedMemorySize() const override { return Buffer.size(); } }; |