rc_buf.h 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095
  1. #pragma once
  2. #include <atomic>
  3. #include <new>
  4. #include <util/generic/ptr.h>
  5. #include <util/generic/string.h>
  6. #include <util/generic/hash_set.h>
  7. #include <util/generic/scope.h>
  8. #include <util/stream/str.h>
  9. #include <util/system/sanitizers.h>
  10. #include <util/system/valgrind.h>
  11. #include <util/generic/array_ref.h>
  12. #include <util/system/sys_alloc.h>
  13. #include "shared_data.h"
  14. #include "rc_buf_backend.h"
  15. #ifdef KIKIMR_TRACE_CONTIGUOUS_DATA_GROW
  16. #include "shared_data_backtracing_owner.h"
  17. #endif
  18. namespace NContiguousDataDetails {
  19. template<typename TContainer>
  20. struct TContainerTraits {
  21. static char* UnsafeGetDataMut(const TContainer& backend) {
  22. return const_cast<char*>(backend.data());
  23. }
  24. };
  25. } // NContiguousDataDetails
  26. class TContiguousSpan
  27. {
  28. private:
  29. const char *Data = nullptr;
  30. size_t Size = 0;
  31. public:
  32. TContiguousSpan() = default;
  33. TContiguousSpan(const char *data, size_t size)
  34. : Data(data)
  35. , Size(size)
  36. {}
  37. TContiguousSpan(const TString& str)
  38. : Data(str.data())
  39. , Size(str.size())
  40. {}
  41. TContiguousSpan(const TStringBuf& str)
  42. : Data(str.data())
  43. , Size(str.size())
  44. {}
  45. TContiguousSpan(const TArrayRef<char>& ref)
  46. : Data(ref.data())
  47. , Size(ref.size())
  48. {}
  49. TContiguousSpan(const TArrayRef<const char>& ref)
  50. : Data(ref.data())
  51. , Size(ref.size())
  52. {}
  53. TContiguousSpan(const NActors::TSharedData& data)
  54. : Data(data.data())
  55. , Size(data.size())
  56. {}
  57. const char& operator[](size_t index) const {
  58. Y_VERIFY_DEBUG(index < Size);
  59. return Data[index];
  60. }
  61. const char *data() const noexcept {
  62. return Data;
  63. }
  64. size_t size() const noexcept {
  65. return Size;
  66. }
  67. const char *GetData() const noexcept {
  68. return Data;
  69. }
  70. size_t GetSize() const noexcept {
  71. return Size;
  72. }
  73. TContiguousSpan SubSpan(size_t pos, size_t n) const noexcept {
  74. pos = Min(pos, size());
  75. n = Min(n, size() - pos);
  76. return TContiguousSpan(data() + pos, n);
  77. }
  78. template<std::size_t Index>
  79. auto get() const noexcept
  80. {
  81. static_assert(Index < 2,
  82. "Index out of bounds for TContiguousSpan");
  83. if constexpr (Index == 0) return Data;
  84. if constexpr (Index == 1) return Size;
  85. }
  86. friend bool operator==(const TContiguousSpan& x, const TContiguousSpan& y) { return Compare(x, y) == 0; }
  87. friend bool operator!=(const TContiguousSpan& x, const TContiguousSpan& y) { return Compare(x, y) != 0; }
  88. friend bool operator< (const TContiguousSpan& x, const TContiguousSpan& y) { return Compare(x, y) < 0; }
  89. friend bool operator<=(const TContiguousSpan& x, const TContiguousSpan& y) { return Compare(x, y) <= 0; }
  90. friend bool operator> (const TContiguousSpan& x, const TContiguousSpan& y) { return Compare(x, y) > 0; }
  91. friend bool operator>=(const TContiguousSpan& x, const TContiguousSpan& y) { return Compare(x, y) >= 0; }
  92. private:
  93. static int Compare(const TContiguousSpan& x, const TContiguousSpan& y) {
  94. if (int res = std::memcmp(x.data(), y.data(), std::min(x.size(), y.size())); res) {
  95. return res;
  96. }
  97. return x.size() - y.size();
  98. }
  99. };
  100. namespace std
  101. {
  102. template<>
  103. struct tuple_size<::TContiguousSpan>
  104. : integral_constant<size_t, 2> {};
  105. template<>
  106. struct tuple_element<0, ::TContiguousSpan>
  107. {
  108. using type = const char *;
  109. };
  110. template<>
  111. struct tuple_element<1, ::TContiguousSpan>
  112. {
  113. using type = size_t;
  114. };
  115. }
  116. template <
  117. class TLeft,
  118. class TRight,
  119. typename std::enable_if<std::is_convertible<TLeft,TContiguousSpan>::value>::type* = nullptr,
  120. typename std::enable_if<std::is_convertible<TRight,TContiguousSpan>::value>::type* = nullptr
  121. >
  122. bool operator==(const TLeft& lhs, const TRight& rhs) {
  123. return TContiguousSpan(lhs) == TContiguousSpan(rhs);
  124. }
  125. template <
  126. class TLeft,
  127. class TRight,
  128. typename std::enable_if<std::is_convertible<TLeft,TContiguousSpan>::value>::type* = nullptr,
  129. typename std::enable_if<std::is_convertible<TRight,TContiguousSpan>::value>::type* = nullptr
  130. >
  131. bool operator!=(const TLeft& lhs, const TRight& rhs) {
  132. return TContiguousSpan(lhs) != TContiguousSpan(rhs);
  133. }
  134. template <
  135. class TLeft,
  136. class TRight,
  137. typename std::enable_if<std::is_convertible<TLeft,TContiguousSpan>::value>::type* = nullptr,
  138. typename std::enable_if<std::is_convertible<TRight,TContiguousSpan>::value>::type* = nullptr
  139. >
  140. bool operator<(const TLeft& lhs, const TRight& rhs) {
  141. return TContiguousSpan(lhs) < TContiguousSpan(rhs);
  142. }
  143. template <
  144. class TLeft,
  145. class TRight,
  146. typename std::enable_if<std::is_convertible<TLeft,TContiguousSpan>::value>::type* = nullptr,
  147. typename std::enable_if<std::is_convertible<TRight,TContiguousSpan>::value>::type* = nullptr
  148. >
  149. bool operator<=(const TLeft& lhs, const TRight& rhs) {
  150. return TContiguousSpan(lhs) <= TContiguousSpan(rhs);
  151. }
  152. template <
  153. class TLeft,
  154. class TRight,
  155. typename std::enable_if<std::is_convertible<TLeft,TContiguousSpan>::value>::type* = nullptr,
  156. typename std::enable_if<std::is_convertible<TRight,TContiguousSpan>::value>::type* = nullptr
  157. >
  158. bool operator>(const TLeft& lhs, const TRight& rhs) {
  159. return TContiguousSpan(lhs) > TContiguousSpan(rhs);
  160. }
  161. template <
  162. class TLeft,
  163. class TRight,
  164. typename std::enable_if<std::is_convertible<TLeft,TContiguousSpan>::value>::type* = nullptr,
  165. typename std::enable_if<std::is_convertible<TRight,TContiguousSpan>::value>::type* = nullptr
  166. >
  167. bool operator>=(const TLeft& lhs, const TRight& rhs) {
  168. return TContiguousSpan(lhs) >= TContiguousSpan(rhs);
  169. }
  170. class TMutableContiguousSpan
  171. {
  172. private:
  173. char *Data = nullptr;
  174. size_t Size = 0;
  175. public:
  176. TMutableContiguousSpan() = default;
  177. TMutableContiguousSpan(char *data, size_t size)
  178. : Data(data)
  179. , Size(size)
  180. {}
  181. char *data() noexcept {
  182. return Data;
  183. }
  184. char *GetData() noexcept {
  185. return Data;
  186. }
  187. TMutableContiguousSpan SubSpan(size_t pos, size_t n) noexcept {
  188. pos = Min(pos, size());
  189. n = Min(n, size() - pos);
  190. return TMutableContiguousSpan(data() + pos, n);
  191. }
  192. const char *data() const noexcept {
  193. return Data;
  194. }
  195. size_t size() const noexcept {
  196. return Size;
  197. }
  198. const char *GetData() const noexcept {
  199. return Data;
  200. }
  201. size_t GetSize() const noexcept {
  202. return Size;
  203. }
  204. TContiguousSpan SubSpan(size_t pos, size_t n) const noexcept {
  205. pos = Min(pos, size());
  206. n = Min(n, size() - pos);
  207. return TContiguousSpan(data() + pos, n);
  208. }
  209. operator TContiguousSpan() const noexcept {
  210. return TContiguousSpan(Data, Size);
  211. }
  212. };
  213. struct IContiguousChunk : TThrRefBase {
  214. using TPtr = TIntrusivePtr<IContiguousChunk>;
  215. virtual ~IContiguousChunk() = default;
  216. /**
  217. * Should give immutable access to data
  218. */
  219. virtual TContiguousSpan GetData() const = 0;
  220. /**
  221. * Should give mutable access to underlying data
  222. * If data is shared - data should be copied
  223. * E.g. for TString str.Detach() should be used
  224. * Possibly invalidates previous *GetData*() calls
  225. */
  226. virtual TMutableContiguousSpan GetDataMut() = 0;
  227. /**
  228. * Should give mutable access to undelying data as fast as possible
  229. * Even if data is shared this property should be ignored
  230. * E.g. in TString const_cast<char *>(str.data()) should be used
  231. * Possibly invalidates previous *GetData*() calls
  232. */
  233. virtual TMutableContiguousSpan UnsafeGetDataMut() {
  234. return GetDataMut();
  235. }
  236. virtual size_t GetOccupiedMemorySize() const = 0;
  237. };
  238. class TRope;
  239. class TRopeArena;
  240. class TRcBuf {
  241. friend class TRope;
  242. friend class TRopeArena;
  243. using TInternalBackend = NDetail::TRcBufInternalBackend;
  244. class TBackend {
  245. enum class EType : uintptr_t {
  246. STRING,
  247. SHARED_DATA,
  248. INTERNAL_BACKEND,
  249. EXTERNAL_BACKEND,
  250. };
  251. struct TBackendHolder {
  252. uintptr_t Data[2];
  253. operator bool() const noexcept {
  254. return Data[0] || Data[1];
  255. }
  256. };
  257. constexpr static TBackendHolder Empty = {0, 0};
  258. #ifndef TSTRING_IS_STD_STRING
  259. static_assert(sizeof(TBackendHolder) >= sizeof(TString));
  260. #endif
  261. static_assert(sizeof(TBackendHolder) >= sizeof(NActors::TSharedData));
  262. static_assert(sizeof(TBackendHolder) >= sizeof(TInternalBackend));
  263. TBackendHolder Owner = TBackend::Empty; // lower bits contain type of the owner
  264. public:
  265. using TCookies = TInternalBackend::TCookies;
  266. static constexpr struct TControlToken {} ControlToken;
  267. static constexpr size_t CookiesSize = sizeof(TCookies);
  268. TBackend() = default;
  269. TBackend(const TBackend& other)
  270. : Owner(other.Owner ? Clone(other.Owner) : TBackend::Empty)
  271. {}
  272. TBackend(TBackend&& other)
  273. : Owner(std::exchange(other.Owner, TBackend::Empty))
  274. {}
  275. TBackend(TString s)
  276. : Owner(Construct<TString>(EType::STRING, std::move(s)))
  277. {}
  278. TBackend(NActors::TSharedData s)
  279. : Owner(Construct<NActors::TSharedData>(EType::SHARED_DATA, std::move(s)))
  280. {}
  281. TBackend(TInternalBackend backend)
  282. : Owner(Construct<TInternalBackend>(EType::INTERNAL_BACKEND, std::move(backend)))
  283. {}
  284. TBackend(IContiguousChunk::TPtr backend)
  285. : Owner(Construct<IContiguousChunk::TPtr>(EType::EXTERNAL_BACKEND, std::move(backend)))
  286. {}
  287. ~TBackend() {
  288. if (Owner) {
  289. Destroy(Owner);
  290. }
  291. }
  292. TBackend& operator =(const TBackend& other) {
  293. if (Y_UNLIKELY(this == &other)) {
  294. return *this;
  295. }
  296. if (Owner) {
  297. Destroy(Owner);
  298. }
  299. if (other.Owner) {
  300. Owner = Clone(other.Owner);
  301. } else {
  302. Owner = TBackend::Empty;
  303. }
  304. return *this;
  305. }
  306. TBackend& operator =(TBackend&& other) {
  307. if (Y_UNLIKELY(this == &other)) {
  308. return *this;
  309. }
  310. if (Owner) {
  311. Destroy(Owner);
  312. }
  313. Owner = std::exchange(other.Owner, TBackend::Empty);
  314. return *this;
  315. }
  316. bool operator ==(const TBackend& other) const {
  317. return Owner == other.Owner;
  318. }
  319. const void *UniqueId() const {
  320. return reinterpret_cast<const void*>(Owner.Data[0]);
  321. }
  322. TCookies* GetCookies() {
  323. if(!Owner) {
  324. return nullptr;
  325. }
  326. return Visit(Owner, [](EType, auto& value) -> TCookies* {
  327. using T = std::decay_t<decltype(value)>;
  328. if constexpr (std::is_same_v<T, TInternalBackend>) {
  329. return value.GetCookies();
  330. } else {
  331. return nullptr;
  332. }
  333. });
  334. }
  335. const TCookies* GetCookies() const {
  336. return const_cast<TBackend&>(*this).GetCookies();
  337. }
  338. bool IsPrivate() const {
  339. if(!Owner) {
  340. return true;
  341. }
  342. return Visit(Owner, [](EType, auto& value) -> bool {
  343. using T = std::decay_t<decltype(value)>;
  344. if constexpr (std::is_same_v<T, NActors::TSharedData> || std::is_same_v<T, TInternalBackend>) {
  345. return value.IsPrivate();
  346. } else {
  347. return false;
  348. }
  349. });
  350. }
  351. TContiguousSpan GetData() const {
  352. if (!Owner) {
  353. return TContiguousSpan();
  354. }
  355. return Visit(Owner, [](EType, auto& value) -> TContiguousSpan {
  356. using T = std::decay_t<decltype(value)>;
  357. if constexpr (std::is_same_v<T, TString>) {
  358. return {&(*value.cbegin()), value.size()};
  359. } else if constexpr (std::is_same_v<T, NActors::TSharedData> || std::is_same_v<T, TInternalBackend>) {
  360. return {value.data(), value.size()};
  361. } else if constexpr (std::is_same_v<T, IContiguousChunk::TPtr>) {
  362. return value->GetData();
  363. } else {
  364. return {};
  365. }
  366. });
  367. }
  368. TMutableContiguousSpan GetDataMut() {
  369. if (!Owner) {
  370. return TMutableContiguousSpan();
  371. }
  372. return Visit(Owner, [](EType, auto& value) -> TMutableContiguousSpan {
  373. using T = std::decay_t<decltype(value)>;
  374. if constexpr (std::is_same_v<T, TString>) {
  375. return {value.Detach(), value.size()};
  376. } else if constexpr (std::is_same_v<T, NActors::TSharedData>) {
  377. if (value.IsShared()) {
  378. value = NActors::TSharedData::Copy(value.data(), value.size());
  379. }
  380. return {value.mutable_data(), value.size()};
  381. } else if constexpr (std::is_same_v<T, TInternalBackend>) {
  382. if (value.IsShared()) {
  383. value = TInternalBackend::Copy(value.data(), value.size());
  384. }
  385. return {value.mutable_data(), value.size()};
  386. } else if constexpr (std::is_same_v<T, IContiguousChunk::TPtr>) {
  387. return value->GetDataMut();
  388. } else {
  389. return {};
  390. }
  391. });
  392. }
  393. TMutableContiguousSpan UnsafeGetDataMut() const {
  394. if (!Owner) {
  395. return TMutableContiguousSpan();
  396. }
  397. return Visit(Owner, [](EType, auto& value) -> TMutableContiguousSpan {
  398. using T = std::decay_t<decltype(value)>;
  399. if constexpr (std::is_same_v<T, TString>) {
  400. return {const_cast<char*>(value.data()), value.size()};
  401. } else if constexpr (std::is_same_v<T, NActors::TSharedData> || std::is_same_v<T, TInternalBackend>) {
  402. return {const_cast<char*>(value.data()), value.size()};
  403. } else if constexpr (std::is_same_v<T, IContiguousChunk::TPtr>) {
  404. return value->UnsafeGetDataMut();
  405. } else {
  406. return {};
  407. }
  408. });
  409. }
  410. size_t GetOccupiedMemorySize() const {
  411. if (!Owner) {
  412. return 0;
  413. }
  414. return Visit(Owner, [](EType, auto& value) {
  415. using T = std::decay_t<decltype(value)>;
  416. if constexpr (std::is_same_v<T, TString>) {
  417. return value.capacity();
  418. } else if constexpr (std::is_same_v<T, NActors::TSharedData> || std::is_same_v<T, TInternalBackend>) {
  419. return value.size(); // There is no capacity
  420. } else if constexpr (std::is_same_v<T, IContiguousChunk::TPtr>) {
  421. return value->GetOccupiedMemorySize();
  422. } else {
  423. Y_FAIL();
  424. }
  425. });
  426. }
  427. template <class TType>
  428. bool ContainsNativeType() const {
  429. if (!Owner) {
  430. return false;
  431. }
  432. return Visit(Owner, [](EType, auto& value) {
  433. using T = std::decay_t<decltype(value)>;
  434. return std::is_same_v<T, TType>;
  435. });
  436. }
  437. bool CanGrowFront(const char* begin) const {
  438. if (!Owner) {
  439. return false;
  440. }
  441. const TCookies* cookies = GetCookies();
  442. return cookies && (IsPrivate() || cookies->Begin.load() == begin);
  443. }
  444. bool CanGrowBack(const char* end) const {
  445. if (!Owner) {
  446. return false;
  447. }
  448. const TCookies* cookies = GetCookies();
  449. return cookies && (IsPrivate() || cookies->End.load() == end);
  450. }
  451. void UpdateCookiesUnsafe(const char* contBegin, const char* contEnd) {
  452. if (!Owner) {
  453. return;
  454. }
  455. TCookies* cookies = GetCookies();
  456. if (cookies) {
  457. cookies->Begin.store(contBegin);
  458. cookies->End.store(contEnd);
  459. }
  460. }
  461. bool UpdateCookiesBegin(const char* curBegin, const char* contBegin) {
  462. if (!Owner) {
  463. return false;
  464. }
  465. TCookies* cookies = GetCookies();
  466. if (cookies) {
  467. return cookies->Begin.compare_exchange_weak(curBegin, contBegin);
  468. }
  469. return false;
  470. }
  471. bool UpdateCookiesEnd(const char* curEnd, const char* contEnd) {
  472. if (!Owner) {
  473. return false;
  474. }
  475. TCookies* cookies = GetCookies();
  476. if (cookies) {
  477. return cookies->End.compare_exchange_weak(curEnd, contEnd);
  478. }
  479. return false;
  480. }
  481. template <class TResult>
  482. TResult GetRaw() const {
  483. if (!Owner) {
  484. return TResult{};
  485. }
  486. return Visit(Owner, [](EType, auto& value) {
  487. using T = std::decay_t<decltype(value)>;
  488. if constexpr (std::is_same_v<T, TResult>) {
  489. return value;
  490. } else {
  491. Y_FAIL();
  492. return TResult{}; // unreachable
  493. }
  494. });
  495. }
  496. NActors::TSharedData GetRawTrimmed(size_t size) const {
  497. NActors::TSharedData result = GetRaw<NActors::TSharedData>();
  498. result.TrimBack(size);
  499. return result;
  500. }
  501. explicit operator bool() const {
  502. return Owner;
  503. }
  504. private:
  505. static constexpr uintptr_t TypeMask = (1 << 3) - 1;
  506. static constexpr uintptr_t ValueMask = ~TypeMask;
  507. template<typename T>
  508. struct TObjectHolder {
  509. struct TWrappedObject : TThrRefBase {
  510. T Value;
  511. TWrappedObject(T&& value)
  512. : Value(std::move(value))
  513. {}
  514. };
  515. TIntrusivePtr<TWrappedObject> Object;
  516. TObjectHolder(T&& object)
  517. : Object(MakeIntrusive<TWrappedObject>(std::move(object)))
  518. {}
  519. };
  520. template<typename TObject>
  521. static TBackendHolder Construct(EType type, TObject&& object) {
  522. if constexpr (sizeof(TObject) <= sizeof(TBackendHolder)) {
  523. TBackendHolder res = TBackend::Empty;
  524. new(&res) std::decay_t<TObject>(std::forward<TObject>(object));
  525. Y_VERIFY_DEBUG((res.Data[0] & ValueMask) == res.Data[0]);
  526. res.Data[0] = res.Data[0] | static_cast<uintptr_t>(type);
  527. return res;
  528. } else {
  529. return Construct<TObjectHolder<TObject>>(type, TObjectHolder<TObject>(std::forward<TObject>(object)));
  530. }
  531. }
  532. template<typename TOwner, typename TCallback, bool IsConst = std::is_const_v<TOwner>>
  533. static std::invoke_result_t<TCallback, EType, std::conditional_t<IsConst, const TString&, TString&>> VisitRaw(TOwner& origValue, TCallback&& callback) {
  534. Y_VERIFY_DEBUG(origValue);
  535. const EType type = static_cast<EType>(origValue.Data[0] & TypeMask);
  536. TBackendHolder value(origValue);
  537. value.Data[0] = value.Data[0] & ValueMask;
  538. // bring object type back
  539. Y_SCOPE_EXIT(&value, &origValue, type){
  540. if constexpr(!IsConst) {
  541. value.Data[0] = value.Data[0] | static_cast<uintptr_t>(type);
  542. origValue = value;
  543. } else {
  544. Y_UNUSED(value);
  545. Y_UNUSED(origValue);
  546. Y_UNUSED(type);
  547. }
  548. };
  549. auto caller = [&](auto& value) { return std::invoke(std::forward<TCallback>(callback), type, value); };
  550. auto wrapper = [&](auto& value) {
  551. using T = std::decay_t<decltype(value)>;
  552. if constexpr (sizeof(T) <= sizeof(TBackendHolder)) {
  553. return caller(value);
  554. } else {
  555. return caller(reinterpret_cast<std::conditional_t<IsConst, const TObjectHolder<T>&, TObjectHolder<T>&>>(value));
  556. }
  557. };
  558. switch (type) {
  559. case EType::STRING: return wrapper(reinterpret_cast<std::conditional_t<IsConst, const TString&, TString&>>(value));
  560. case EType::SHARED_DATA: return wrapper(reinterpret_cast<std::conditional_t<IsConst, const NActors::TSharedData&, NActors::TSharedData&>>(value));
  561. case EType::INTERNAL_BACKEND: return wrapper(reinterpret_cast<std::conditional_t<IsConst, const TInternalBackend&, TInternalBackend&>>(value));
  562. case EType::EXTERNAL_BACKEND: return wrapper(reinterpret_cast<std::conditional_t<IsConst, const IContiguousChunk::TPtr&, IContiguousChunk::TPtr&>>(value));
  563. }
  564. Y_FAIL("Unexpected type# %" PRIu64, static_cast<ui64>(type));
  565. }
  566. template<typename TOwner, typename TCallback, bool IsConst = std::is_const_v<TOwner>>
  567. static std::invoke_result_t<TCallback, EType, std::conditional_t<IsConst, const TString&, TString&>> Visit(TOwner& value, TCallback&& callback) {
  568. return VisitRaw(value, [&](EType type, auto& value) {
  569. return std::invoke(std::forward<TCallback>(callback), type, Unwrap(value));
  570. });
  571. }
  572. template<typename T> static T& Unwrap(T& object) { return object; }
  573. template<typename T> static T& Unwrap(TObjectHolder<T>& holder) { return holder.Object->Value; }
  574. template<typename T> static const T& Unwrap(const TObjectHolder<T>& holder) { return holder.Object->Value; }
  575. template<typename TOwner>
  576. static TBackendHolder Clone(TOwner& value) {
  577. return VisitRaw(value, [](EType type, auto& value) { return Construct(type, value); });
  578. }
  579. template<typename TOwner>
  580. static void Destroy(TOwner& value) {
  581. VisitRaw(value, [](EType, auto& value) { CallDtor(value); });
  582. }
  583. template<typename T>
  584. static void CallDtor(T& value) {
  585. value.~T();
  586. }
  587. };
  588. static constexpr struct TOwnedPiece {} OwnedPiece{};
  589. TBackend Backend; // who actually holds the data
  590. const char *Begin; // data start
  591. const char *End; // data end
  592. explicit TRcBuf(TInternalBackend s, const char *data, size_t size)
  593. : Backend(std::move(s))
  594. {
  595. Y_VERIFY(Backend.GetData().data() == nullptr ||
  596. (Backend.GetCookies() && Backend.GetCookies()->Begin == data && Backend.GetCookies()->End == data + size));
  597. Begin = data;
  598. End = data + size;
  599. }
  600. explicit TRcBuf(TInternalBackend s)
  601. : Backend(std::move(s))
  602. {
  603. auto span = Backend.GetData();
  604. Begin = span.data();
  605. End = Begin + span.size();
  606. }
  607. TRcBuf(TOwnedPiece, const char *data, size_t size, const TRcBuf& from)
  608. : TRcBuf(from.Backend, {data, size})
  609. {
  610. Y_VERIFY(data >= from.GetData());
  611. Y_VERIFY(data < from.GetData() + from.GetSize());
  612. Y_VERIFY(data + size <= from.GetData() + from.GetSize());
  613. Backend.UpdateCookiesUnsafe(Begin, End);
  614. }
  615. TRcBuf(TOwnedPiece, const char *begin, const char *end, const TRcBuf& from)
  616. : TRcBuf(OwnedPiece, begin, end - begin, from)
  617. {}
  618. public:
  619. static constexpr struct TPiece {} Piece{};
  620. enum class EResizeResult {
  621. NoAlloc,
  622. Alloc,
  623. };
  624. enum class EResizeStrategy {
  625. KeepRooms,
  626. FailOnCopy,
  627. // SaveAllocs, // Move data if there is enough space in (headroom + size + tailroom)
  628. };
  629. TRcBuf()
  630. : Begin(nullptr)
  631. , End(nullptr)
  632. {}
  633. template<typename T>
  634. TRcBuf(T&& backend, const TContiguousSpan& data)
  635. : Backend(std::forward<T>(backend))
  636. , Begin(data.data())
  637. , End(Begin + data.size())
  638. {}
  639. explicit TRcBuf(TString s)
  640. : Backend(std::move(s))
  641. {
  642. auto span = Backend.GetData();
  643. Begin = span.data();
  644. End = Begin + span.size();
  645. }
  646. explicit TRcBuf(NActors::TSharedData s)
  647. : Backend(std::move(s))
  648. {
  649. auto span = Backend.GetData();
  650. Begin = span.data();
  651. End = Begin + span.size();
  652. }
  653. TRcBuf(IContiguousChunk::TPtr backend)
  654. : TRcBuf(backend, backend->GetData())
  655. {}
  656. TRcBuf(TPiece, const char *data, size_t size, const TRcBuf& from)
  657. : TRcBuf(from.Backend, {data, size})
  658. {
  659. Y_VERIFY(data >= from.GetData());
  660. Y_VERIFY(data < from.GetData() + from.GetSize());
  661. Y_VERIFY(data + size <= from.GetData() + from.GetSize());
  662. }
  663. TRcBuf(TPiece, const char *begin, const char *end, const TRcBuf& from)
  664. : TRcBuf(Piece, begin, end - begin, from)
  665. {}
  666. TRcBuf(const TRcBuf& other)
  667. : Backend(other.Backend)
  668. , Begin(other.Begin)
  669. , End(other.End)
  670. {}
  671. TRcBuf(TRcBuf&& other)
  672. : Backend(std::move(other.Backend))
  673. , Begin(other.Begin)
  674. , End(other.End)
  675. {}
  676. TRcBuf& operator =(const TRcBuf&) = default;
  677. TRcBuf& operator =(TRcBuf&&) = default;
  678. static TRcBuf Uninitialized(size_t size, size_t headroom = 0, size_t tailroom = 0)
  679. {
  680. if (size == 0) {
  681. return TRcBuf();
  682. }
  683. if (headroom == 0 && tailroom == 0) {
  684. TInternalBackend res = TInternalBackend::Uninitialized(size);
  685. return TRcBuf(
  686. OwnedPiece,
  687. res.data(),
  688. res.data() + res.size(),
  689. TRcBuf(res));
  690. }
  691. TInternalBackend res = TInternalBackend::Uninitialized(size, headroom, tailroom);
  692. return TRcBuf(res, res.data() + headroom, size);
  693. }
  694. static TRcBuf Copy(TContiguousSpan data, size_t headroom = 0, size_t tailroom = 0) {
  695. TRcBuf res = Uninitialized(data.size(), headroom, tailroom);
  696. std::memcpy(res.UnsafeGetDataMut(), data.GetData(), data.GetSize());
  697. return res;
  698. }
  699. static TRcBuf Copy(const char* data, size_t size, size_t headroom = 0, size_t tailroom = 0) {
  700. return Copy({data, size}, headroom, tailroom);
  701. }
  702. template <class TType>
  703. bool ContainsNativeType() const {
  704. return Backend.ContainsNativeType<TType>();
  705. }
  706. template <class TResult>
  707. TResult GetRaw() const {
  708. return Backend.GetRaw<TResult>();
  709. }
  710. NActors::TSharedData GetRawTrimmed(size_t size) const {
  711. return Backend.GetRawTrimmed(size);
  712. }
  713. bool ReferencesWholeContainer() const {
  714. return Backend.GetData().size() == GetSize();
  715. }
  716. bool ReferencesTrimableToWholeContainer() const {
  717. if (ContainsNativeType<NActors::TSharedData>()) {
  718. return Backend.GetData().size() == (GetSize() + UnsafeTailroom());
  719. } else {
  720. return ReferencesWholeContainer();
  721. }
  722. }
  723. bool CanGrowFront() const noexcept {
  724. return Backend.CanGrowFront(Begin);
  725. }
  726. bool CanGrowBack() const noexcept {
  727. return Backend.CanGrowBack(End);
  728. }
  729. size_t GetSize() const {
  730. return End - Begin;
  731. }
  732. size_t Size() const {
  733. return End - Begin;
  734. }
  735. size_t GetOccupiedMemorySize() const {
  736. return Backend.GetOccupiedMemorySize();
  737. }
  738. const char* GetData() const {
  739. return Begin;
  740. }
  741. char* GetDataMut() {
  742. const char* oldBegin = Backend.GetData().data();
  743. ptrdiff_t offset = Begin - oldBegin;
  744. size_t size = GetSize();
  745. char* newBegin = Backend.GetDataMut().data();
  746. Begin = newBegin + offset;
  747. End = Begin + size;
  748. return newBegin + offset;
  749. }
  750. char* UnsafeGetDataMut() {
  751. const char* oldBegin = Backend.GetData().data();
  752. ptrdiff_t offset = Begin - oldBegin;
  753. size_t size = GetSize();
  754. char* newBegin = Backend.UnsafeGetDataMut().data();
  755. Begin = newBegin + offset;
  756. End = Begin + size;
  757. return newBegin + offset;
  758. }
  759. template <class TResult>
  760. TResult ExtractUnderlyingContainerOrCopy() const {
  761. if (ContainsNativeType<TResult>() && (ReferencesWholeContainer() || ReferencesTrimableToWholeContainer())) {
  762. using T = std::decay_t<TResult>;
  763. if constexpr (std::is_same_v<T, NActors::TSharedData>) {
  764. return GetRawTrimmed(GetSize());
  765. } else {
  766. return GetRaw<TResult>();
  767. }
  768. }
  769. TResult res = TResult::Uninitialized(GetSize());
  770. char* data = NContiguousDataDetails::TContainerTraits<TResult>::UnsafeGetDataMut(res);
  771. std::memcpy(data, Begin, End - Begin);
  772. return res;
  773. }
  774. TContiguousSpan GetContiguousSpan() const {
  775. return {GetData(), GetSize()};
  776. }
  777. TStringBuf Slice(size_t pos = 0, size_t len = -1) const noexcept {
  778. pos = Min(pos, size());
  779. len = Min(len, size() - pos);
  780. return {const_cast<TRcBuf*>(this)->UnsafeGetDataMut() + pos, len};
  781. }
  782. explicit operator TStringBuf() const noexcept {
  783. return Slice();
  784. }
  785. TMutableContiguousSpan GetContiguousSpanMut() {
  786. return {GetDataMut(), GetSize()};
  787. }
  788. TMutableContiguousSpan UnsafeGetContiguousSpanMut() {
  789. return {UnsafeGetDataMut(), GetSize()};
  790. }
  791. bool HasBuffer() const {
  792. return static_cast<bool>(Backend);
  793. }
  794. size_t size() const {
  795. return GetSize();
  796. }
  797. bool empty() const {
  798. return !static_cast<bool>(Backend);
  799. }
  800. operator bool() const {
  801. return !empty();
  802. }
  803. const char* data() const {
  804. return GetData();
  805. }
  806. const char* begin() const {
  807. return Begin;
  808. }
  809. const char* end() const {
  810. return End;
  811. }
  812. char& operator[](size_t pos) {
  813. return UnsafeGetDataMut()[pos];
  814. }
  815. const char& operator[](size_t pos) const {
  816. return GetData()[pos];
  817. }
  818. void reserve(size_t size) {
  819. ReserveTailroom(size);
  820. }
  821. void ReserveHeadroom(size_t size) {
  822. if (Headroom() >= size) {
  823. return;
  824. }
  825. auto newData = TRcBuf::Uninitialized(GetSize(), size, UnsafeTailroom());
  826. if (auto data = GetData(); data) {
  827. std::memcpy(newData.UnsafeGetDataMut(), GetData(), GetSize());
  828. }
  829. *this = std::move(newData);
  830. }
  831. void ReserveTailroom(size_t size) {
  832. if (Tailroom() >= size) {
  833. return;
  834. }
  835. auto newData = TRcBuf::Uninitialized(GetSize(), UnsafeHeadroom(), size);
  836. if (auto data = GetData(); data) {
  837. std::memcpy(newData.UnsafeGetDataMut(), GetData(), GetSize());
  838. }
  839. *this = std::move(newData);
  840. }
  841. void ReserveBidi(size_t headroom, size_t tailroom) {
  842. if (Headroom() >= headroom && Tailroom() >= tailroom) {
  843. return;
  844. }
  845. auto newData = TRcBuf::Uninitialized(
  846. GetSize(),
  847. std::max(UnsafeHeadroom(), headroom),
  848. std::max(UnsafeTailroom(), tailroom));
  849. if (auto data = GetData(); data) {
  850. std::memcpy(newData.UnsafeGetDataMut(), GetData(), GetSize());
  851. }
  852. *this = std::move(newData);
  853. }
  854. EResizeResult GrowFront(size_t size, EResizeStrategy strategy = EResizeStrategy::KeepRooms) {
  855. if (Headroom() >= size && Backend.UpdateCookiesBegin(Begin, Begin - size)) {
  856. Begin -= size;
  857. return EResizeResult::NoAlloc;
  858. } else {
  859. if (strategy == EResizeStrategy::FailOnCopy && static_cast<bool>(Backend)) {
  860. Y_FAIL("Fail on grow");
  861. }
  862. auto newData = TRcBuf::Uninitialized(size + GetSize(), UnsafeHeadroom() > size ? UnsafeHeadroom() - size : 0, UnsafeTailroom());
  863. if (auto data = GetData(); data) {
  864. std::memcpy(newData.UnsafeGetDataMut() + size, GetData(), GetSize());
  865. }
  866. *this = std::move(newData);
  867. return EResizeResult::Alloc;
  868. }
  869. }
  870. EResizeResult GrowBack(size_t size, EResizeStrategy strategy = EResizeStrategy::KeepRooms) {
  871. if (Tailroom() > size && Backend.UpdateCookiesEnd(End, End + size)) {
  872. End += size;
  873. return EResizeResult::NoAlloc;
  874. } else {
  875. if (strategy == EResizeStrategy::FailOnCopy && static_cast<bool>(Backend)) {
  876. Y_FAIL("Fail on grow");
  877. }
  878. auto newData = TRcBuf::Uninitialized(size + GetSize(), UnsafeHeadroom(), UnsafeTailroom() > size ? UnsafeTailroom() - size : 0);
  879. if (auto data = GetData(); data) {
  880. std::memcpy(newData.UnsafeGetDataMut(), GetData(), GetSize());
  881. }
  882. *this = std::move(newData);
  883. return EResizeResult::Alloc;
  884. }
  885. }
  886. void TrimBack(size_t size) {
  887. Y_VERIFY(size <= GetSize());
  888. End = End - (GetSize() - size);
  889. }
  890. void TrimFront(size_t size) {
  891. Y_VERIFY(size <= GetSize());
  892. Begin = Begin + (GetSize() - size);
  893. }
  894. char* Detach() {
  895. return GetDataMut();
  896. }
  897. size_t UnsafeHeadroom() const {
  898. return Begin - Backend.GetData().data();
  899. }
  900. size_t UnsafeTailroom() const {
  901. auto span = Backend.GetData();
  902. return (span.GetData() + span.GetSize()) - End;
  903. }
  904. size_t Headroom() const {
  905. if (Backend.CanGrowFront(Begin)) {
  906. return UnsafeHeadroom();
  907. }
  908. return 0;
  909. }
  910. size_t Tailroom() const {
  911. if (Backend.CanGrowBack(End)) {
  912. return UnsafeTailroom();
  913. }
  914. return 0;
  915. }
  916. operator TContiguousSpan() const noexcept {
  917. return TContiguousSpan(GetData(), GetSize());
  918. }
  919. explicit operator TMutableContiguousSpan() noexcept {
  920. return TMutableContiguousSpan(GetDataMut(), GetSize());
  921. }
  922. };