aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/actors
diff options
context:
space:
mode:
authorinnokentii <innokentii@yandex-team.com>2022-09-29 17:04:39 +0300
committerinnokentii <innokentii@yandex-team.com>2022-09-29 17:04:39 +0300
commitb9c33d2970daa903dfdd7cfd222c293593e83536 (patch)
tree8adc97a90bb850ba7e5c0b6dc48959709ed89dd0 /library/cpp/actors
parentd562e7c54fcae2c816d67834310ecfec90e8a69d (diff)
downloadydb-b9c33d2970daa903dfdd7cfd222c293593e83536.tar.gz
Extend TContiguousData interface
extend TContiguousData interface
Diffstat (limited to 'library/cpp/actors')
-rw-r--r--library/cpp/actors/util/contiguous_data.h315
-rw-r--r--library/cpp/actors/util/contiguous_data_ut.cpp117
-rw-r--r--library/cpp/actors/util/rope.h32
-rw-r--r--library/cpp/actors/util/rope_ut.cpp11
-rw-r--r--library/cpp/actors/util/shared_data_rope_backend.h2
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();
}
};