bin_saver.h 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. #pragma once
  2. #include "buffered_io.h"
  3. #include "class_factory.h"
  4. #include <library/cpp/containers/2d_array/2d_array.h>
  5. #include <util/generic/hash_set.h>
  6. #include <util/generic/buffer.h>
  7. #include <util/generic/list.h>
  8. #include <util/generic/maybe.h>
  9. #include <util/generic/bitmap.h>
  10. #include <util/generic/variant.h>
  11. #include <util/generic/ylimits.h>
  12. #include <util/memory/blob.h>
  13. #include <util/digest/murmur.h>
  14. #include <util/system/compiler.h>
  15. #include <array>
  16. #include <bitset>
  17. #include <list>
  18. #include <string>
  19. #ifdef _MSC_VER
  20. #pragma warning(disable : 4127)
  21. #endif
  22. enum ESaverMode {
  23. SAVER_MODE_READ = 1,
  24. SAVER_MODE_WRITE = 2,
  25. SAVER_MODE_WRITE_COMPRESSED = 3,
  26. };
  27. namespace NBinSaverInternals {
  28. // This lets explicitly control the overload resolution priority
  29. // The higher P means higher priority in overload resolution order
  30. template <int P>
  31. struct TOverloadPriority : TOverloadPriority <P-1> {
  32. };
  33. template <>
  34. struct TOverloadPriority<0> {
  35. };
  36. }
  37. //////////////////////////////////////////////////////////////////////////
  38. struct IBinSaver {
  39. public:
  40. typedef unsigned char chunk_id;
  41. typedef ui32 TStoredSize; // changing this will break compatibility
  42. private:
  43. // This overload is required to avoid infinite recursion when overriding serialization in derived classes:
  44. // struct B {
  45. // virtual int operator &(IBinSaver& f) {
  46. // return 0;
  47. // }
  48. // };
  49. //
  50. // struct D : B {
  51. // int operator &(IBinSaver& f) override {
  52. // f.Add(0, static_cast<B*>(this));
  53. // return 0;
  54. // }
  55. // };
  56. template <class T, typename = decltype(std::declval<T*>()->T::operator&(std::declval<IBinSaver&>()))>
  57. void CallObjectSerialize(T* p, NBinSaverInternals::TOverloadPriority<2>) { // highest priority - will be resolved first if enabled
  58. // Note: p->operator &(*this) would lead to infinite recursion
  59. p->T::operator&(*this);
  60. }
  61. template <class T, typename = decltype(std::declval<T&>() & std::declval<IBinSaver&>())>
  62. void CallObjectSerialize(T* p, NBinSaverInternals::TOverloadPriority<1>) { // lower priority - will be resolved second if enabled
  63. (*p) & (*this);
  64. }
  65. template <class T>
  66. void CallObjectSerialize(T* p, NBinSaverInternals::TOverloadPriority<0>) { // lower priority - will be resolved last
  67. #if (!defined(_MSC_VER))
  68. // broken in clang16 for some types
  69. // In MSVC __has_trivial_copy returns false to enums, primitive types and arrays.
  70. // static_assert(__is_trivially_copyable(T), "Class is nontrivial copyable, you must define operator&, see");
  71. #endif
  72. DataChunk(p, sizeof(T));
  73. }
  74. // vector
  75. template <class T, class TA>
  76. void DoVector(TVector<T, TA>& data) {
  77. TStoredSize nSize;
  78. if (IsReading()) {
  79. data.clear();
  80. Add(2, &nSize);
  81. data.resize(nSize);
  82. } else {
  83. nSize = data.size();
  84. CheckOverflow(nSize, data.size());
  85. Add(2, &nSize);
  86. }
  87. for (TStoredSize i = 0; i < nSize; i++)
  88. Add(1, &data[i]);
  89. }
  90. template <class T, int N>
  91. void DoArray(T (&data)[N]) {
  92. for (size_t i = 0; i < N; i++) {
  93. Add(1, &(data[i]));
  94. }
  95. }
  96. template <typename TLarge>
  97. void CheckOverflow(TStoredSize nSize, TLarge origSize) {
  98. if (nSize != origSize) {
  99. fprintf(stderr, "IBinSaver: object size is too large to be serialized (%" PRIu32 " != %" PRIu64 ")\n", nSize, (ui64)origSize);
  100. abort();
  101. }
  102. }
  103. template <class T, class TA>
  104. void DoDataVector(TVector<T, TA>& data) {
  105. TStoredSize nSize = data.size();
  106. CheckOverflow(nSize, data.size());
  107. Add(1, &nSize);
  108. if (IsReading()) {
  109. data.clear();
  110. data.resize(nSize);
  111. }
  112. if (nSize > 0)
  113. DataChunk(&data[0], sizeof(T) * nSize);
  114. }
  115. template <class AM>
  116. void DoAnyMap(AM& data) {
  117. if (IsReading()) {
  118. data.clear();
  119. TStoredSize nSize;
  120. Add(3, &nSize);
  121. TVector<typename AM::key_type, typename std::allocator_traits<typename AM::allocator_type>::template rebind_alloc<typename AM::key_type>> indices;
  122. indices.resize(nSize);
  123. for (TStoredSize i = 0; i < nSize; ++i)
  124. Add(1, &indices[i]);
  125. for (TStoredSize i = 0; i < nSize; ++i)
  126. Add(2, &data[indices[i]]);
  127. } else {
  128. TStoredSize nSize = data.size();
  129. CheckOverflow(nSize, data.size());
  130. Add(3, &nSize);
  131. TVector<typename AM::key_type, typename std::allocator_traits<typename AM::allocator_type>::template rebind_alloc<typename AM::key_type>> indices;
  132. indices.resize(nSize);
  133. TStoredSize i = 1;
  134. for (auto pos = data.begin(); pos != data.end(); ++pos, ++i)
  135. indices[nSize - i] = pos->first;
  136. for (TStoredSize j = 0; j < nSize; ++j)
  137. Add(1, &indices[j]);
  138. for (TStoredSize j = 0; j < nSize; ++j)
  139. Add(2, &data[indices[j]]);
  140. }
  141. }
  142. // hash_multimap
  143. template <class AMM>
  144. void DoAnyMultiMap(AMM& data) {
  145. if (IsReading()) {
  146. data.clear();
  147. TStoredSize nSize;
  148. Add(3, &nSize);
  149. TVector<typename AMM::key_type, typename std::allocator_traits<typename AMM::allocator_type>::template rebind_alloc<typename AMM::key_type>> indices;
  150. indices.resize(nSize);
  151. for (TStoredSize i = 0; i < nSize; ++i)
  152. Add(1, &indices[i]);
  153. for (TStoredSize i = 0; i < nSize; ++i) {
  154. std::pair<typename AMM::key_type, typename AMM::mapped_type> valToInsert;
  155. valToInsert.first = indices[i];
  156. Add(2, &valToInsert.second);
  157. data.insert(valToInsert);
  158. }
  159. } else {
  160. TStoredSize nSize = data.size();
  161. CheckOverflow(nSize, data.size());
  162. Add(3, &nSize);
  163. for (auto pos = data.begin(); pos != data.end(); ++pos)
  164. Add(1, (typename AMM::key_type*)(&pos->first));
  165. for (auto pos = data.begin(); pos != data.end(); ++pos)
  166. Add(2, &pos->second);
  167. }
  168. }
  169. template <class T>
  170. void DoAnySet(T& data) {
  171. if (IsReading()) {
  172. data.clear();
  173. TStoredSize nSize;
  174. Add(2, &nSize);
  175. for (TStoredSize i = 0; i < nSize; ++i) {
  176. typename T::value_type member;
  177. Add(1, &member);
  178. data.insert(member);
  179. }
  180. } else {
  181. TStoredSize nSize = data.size();
  182. CheckOverflow(nSize, data.size());
  183. Add(2, &nSize);
  184. for (const auto& elem : data) {
  185. auto member = elem;
  186. Add(1, &member);
  187. }
  188. }
  189. }
  190. // 2D array
  191. template <class T>
  192. void Do2DArray(TArray2D<T>& a) {
  193. int nXSize = a.GetXSize(), nYSize = a.GetYSize();
  194. Add(1, &nXSize);
  195. Add(2, &nYSize);
  196. if (IsReading())
  197. a.SetSizes(nXSize, nYSize);
  198. for (int i = 0; i < nXSize * nYSize; i++)
  199. Add(3, &a[i / nXSize][i % nXSize]);
  200. }
  201. template <class T>
  202. void Do2DArrayData(TArray2D<T>& a) {
  203. int nXSize = a.GetXSize(), nYSize = a.GetYSize();
  204. Add(1, &nXSize);
  205. Add(2, &nYSize);
  206. if (IsReading())
  207. a.SetSizes(nXSize, nYSize);
  208. if (nXSize * nYSize > 0)
  209. DataChunk(&a[0][0], sizeof(T) * nXSize * nYSize);
  210. }
  211. // strings
  212. template <class TStringType>
  213. void DataChunkStr(TStringType& data, i64 elemSize) {
  214. if (bRead) {
  215. TStoredSize nCount = 0;
  216. File.Read(&nCount, sizeof(TStoredSize));
  217. data.resize(nCount);
  218. if (nCount)
  219. File.Read(&*data.begin(), nCount * elemSize);
  220. } else {
  221. TStoredSize nCount = data.size();
  222. CheckOverflow(nCount, data.size());
  223. File.Write(&nCount, sizeof(TStoredSize));
  224. File.Write(data.c_str(), nCount * elemSize);
  225. }
  226. }
  227. void DataChunkString(std::string& data) {
  228. DataChunkStr(data, sizeof(char));
  229. }
  230. void DataChunkStroka(TString& data) {
  231. DataChunkStr(data, sizeof(TString::char_type));
  232. }
  233. void DataChunkWtroka(TUtf16String& data) {
  234. DataChunkStr(data, sizeof(wchar16));
  235. }
  236. void DataChunk(void* pData, i64 nSize) {
  237. i64 chunkSize = 1 << 30;
  238. for (i64 offset = 0; offset < nSize; offset += chunkSize) {
  239. void* ptr = (char*)pData + offset;
  240. i64 size = offset + chunkSize < nSize ? chunkSize : (nSize - offset);
  241. if (bRead)
  242. File.Read(ptr, size);
  243. else
  244. File.Write(ptr, size);
  245. }
  246. }
  247. // storing/loading pointers to objects
  248. void StoreObject(IObjectBase* pObject);
  249. IObjectBase* LoadObject();
  250. bool bRead;
  251. TBufferedStream<> File;
  252. // maps objects addresses during save(first) to addresses during load(second) - during loading
  253. // or serves as a sign that some object has been already stored - during storing
  254. bool StableOutput;
  255. typedef THashMap<void*, ui32> PtrIdHash;
  256. TAutoPtr<PtrIdHash> PtrIds;
  257. typedef THashMap<ui64, TPtr<IObjectBase>> CObjectsHash;
  258. TAutoPtr<CObjectsHash> Objects;
  259. TVector<IObjectBase*> ObjectQueue;
  260. public:
  261. bool IsReading() {
  262. return bRead;
  263. }
  264. void AddRawData(const chunk_id, void* pData, i64 nSize) {
  265. DataChunk(pData, nSize);
  266. }
  267. // return type of Add() is used to detect specialized serializer (see HasNonTrivialSerializer below)
  268. template <class T>
  269. char Add(const chunk_id, T* p) {
  270. CallObjectSerialize(p, NBinSaverInternals::TOverloadPriority<2>());
  271. return 0;
  272. }
  273. int Add(const chunk_id, std::string* pStr) {
  274. DataChunkString(*pStr);
  275. return 0;
  276. }
  277. int Add(const chunk_id, TString* pStr) {
  278. DataChunkStroka(*pStr);
  279. return 0;
  280. }
  281. int Add(const chunk_id, TUtf16String* pStr) {
  282. DataChunkWtroka(*pStr);
  283. return 0;
  284. }
  285. int Add(const chunk_id, TBlob* blob) {
  286. if (bRead) {
  287. ui64 size = 0;
  288. File.Read(&size, sizeof(size));
  289. TBuffer buffer;
  290. buffer.Advance(size);
  291. if (size > 0)
  292. File.Read(buffer.Data(), buffer.Size());
  293. (*blob) = TBlob::FromBuffer(buffer);
  294. } else {
  295. const ui64 size = blob->Size();
  296. File.Write(&size, sizeof(size));
  297. File.Write(blob->Data(), blob->Size());
  298. }
  299. return 0;
  300. }
  301. template <class T1, class TA>
  302. int Add(const chunk_id, TVector<T1, TA>* pVec) {
  303. if (HasNonTrivialSerializer<T1>(0u))
  304. DoVector(*pVec);
  305. else
  306. DoDataVector(*pVec);
  307. return 0;
  308. }
  309. template <class T, int N>
  310. int Add(const chunk_id, T (*pVec)[N]) {
  311. if (HasNonTrivialSerializer<T>(0u))
  312. DoArray(*pVec);
  313. else
  314. DataChunk(pVec, sizeof(*pVec));
  315. return 0;
  316. }
  317. template <class T1, class T2, class T3, class T4>
  318. int Add(const chunk_id, TMap<T1, T2, T3, T4>* pMap) {
  319. DoAnyMap(*pMap);
  320. return 0;
  321. }
  322. template <class T1, class T2, class T3, class T4, class T5>
  323. int Add(const chunk_id, THashMap<T1, T2, T3, T4, T5>* pHash) {
  324. DoAnyMap(*pHash);
  325. return 0;
  326. }
  327. template <class T1, class T2, class T3, class T4, class T5>
  328. int Add(const chunk_id, THashMultiMap<T1, T2, T3, T4, T5>* pHash) {
  329. DoAnyMultiMap(*pHash);
  330. return 0;
  331. }
  332. template <class K, class L, class A>
  333. int Add(const chunk_id, TSet<K, L, A>* pSet) {
  334. DoAnySet(*pSet);
  335. return 0;
  336. }
  337. template <class T1, class T2, class T3, class T4>
  338. int Add(const chunk_id, THashSet<T1, T2, T3, T4>* pHash) {
  339. DoAnySet(*pHash);
  340. return 0;
  341. }
  342. template <class T1>
  343. int Add(const chunk_id, TArray2D<T1>* pArr) {
  344. if (HasNonTrivialSerializer<T1>(0u))
  345. Do2DArray(*pArr);
  346. else
  347. Do2DArrayData(*pArr);
  348. return 0;
  349. }
  350. template <class T1>
  351. int Add(const chunk_id, TList<T1>* pList) {
  352. TList<T1>& data = *pList;
  353. if (IsReading()) {
  354. int nSize;
  355. Add(2, &nSize);
  356. data.clear();
  357. data.insert(data.begin(), nSize, T1());
  358. } else {
  359. int nSize = data.size();
  360. Add(2, &nSize);
  361. }
  362. int i = 1;
  363. for (typename TList<T1>::iterator k = data.begin(); k != data.end(); ++k, ++i)
  364. Add(i + 2, &(*k));
  365. return 0;
  366. }
  367. template <class T1, class T2>
  368. int Add(const chunk_id, std::pair<T1, T2>* pData) {
  369. Add(1, &(pData->first));
  370. Add(2, &(pData->second));
  371. return 0;
  372. }
  373. template <class T1, size_t N>
  374. int Add(const chunk_id, std::array<T1, N>* pData) {
  375. if (HasNonTrivialSerializer<T1>(0u)) {
  376. for (size_t i = 0; i < N; ++i)
  377. Add(1, &(*pData)[i]);
  378. } else {
  379. DataChunk((void*)pData->data(), pData->size() * sizeof(T1));
  380. }
  381. return 0;
  382. }
  383. template <size_t N>
  384. int Add(const chunk_id, std::bitset<N>* pData) {
  385. if (IsReading()) {
  386. std::string s;
  387. Add(1, &s);
  388. *pData = std::bitset<N>(s);
  389. } else {
  390. std::string s = pData->template to_string<char, std::char_traits<char>, std::allocator<char>>();
  391. Add(1, &s);
  392. }
  393. return 0;
  394. }
  395. int Add(const chunk_id, TDynBitMap* pData) {
  396. if (IsReading()) {
  397. ui64 count = 0;
  398. Add(1, &count);
  399. pData->Clear();
  400. pData->Reserve(count * sizeof(TDynBitMap::TChunk) * 8);
  401. for (ui64 i = 0; i < count; ++i) {
  402. TDynBitMap::TChunk chunk = 0;
  403. Add(i + 1, &chunk);
  404. if (i > 0) {
  405. pData->LShift(8 * sizeof(TDynBitMap::TChunk));
  406. }
  407. pData->Or(chunk);
  408. }
  409. } else {
  410. ui64 count = pData->GetChunkCount();
  411. Add(1, &count);
  412. for (ui64 i = 0; i < count; ++i) {
  413. // Write in reverse order
  414. TDynBitMap::TChunk chunk = pData->GetChunks()[count - i - 1];
  415. Add(i + 1, &chunk);
  416. }
  417. }
  418. return 0;
  419. }
  420. template <class TVariantClass>
  421. struct TLoadFromTypeFromListHelper {
  422. template <class T0, class... TTail>
  423. static void Do(IBinSaver& binSaver, ui32 typeIndex, TVariantClass* pData) {
  424. if constexpr (sizeof...(TTail) == 0) {
  425. Y_ASSERT(typeIndex == 0);
  426. T0 chunk;
  427. binSaver.Add(2, &chunk);
  428. *pData = std::move(chunk);
  429. } else {
  430. if (typeIndex == 0) {
  431. Do<T0>(binSaver, 0, pData);
  432. } else {
  433. Do<TTail...>(binSaver, typeIndex - 1, pData);
  434. }
  435. }
  436. }
  437. };
  438. template <class... TVariantTypes>
  439. int Add(const chunk_id, std::variant<TVariantTypes...>* pData) {
  440. static_assert(std::variant_size_v<std::variant<TVariantTypes...>> < Max<ui32>());
  441. ui32 index;
  442. if (IsReading()) {
  443. Add(1, &index);
  444. TLoadFromTypeFromListHelper<std::variant<TVariantTypes...>>::template Do<TVariantTypes...>(
  445. *this,
  446. index,
  447. pData
  448. );
  449. } else {
  450. index = pData->index(); // type cast is safe because of static_assert check above
  451. Add(1, &index);
  452. std::visit([&](auto& dst) -> void { Add(2, &dst); }, *pData);
  453. }
  454. return 0;
  455. }
  456. void AddPolymorphicBase(chunk_id, IObjectBase* pObject) {
  457. (*pObject) & (*this);
  458. }
  459. template <class T1, class T2>
  460. void DoPtr(TPtrBase<T1, T2>* pData) {
  461. if (pData && pData->Get()) {
  462. }
  463. if (IsReading())
  464. pData->Set(CastToUserObject(LoadObject(), (T1*)nullptr));
  465. else
  466. StoreObject(pData->GetBarePtr());
  467. }
  468. template <class T, class TPolicy>
  469. int Add(const chunk_id, TMaybe<T, TPolicy>* pData) {
  470. TMaybe<T, TPolicy>& data = *pData;
  471. if (IsReading()) {
  472. bool defined = false;
  473. Add(1, &defined);
  474. if (defined) {
  475. data = T();
  476. Add(2, data.Get());
  477. }
  478. } else {
  479. bool defined = data.Defined();
  480. Add(1, &defined);
  481. if (defined) {
  482. Add(2, data.Get());
  483. }
  484. }
  485. return 0;
  486. }
  487. template <typename TOne>
  488. void AddMulti(TOne& one) {
  489. Add(0, &one);
  490. }
  491. template <typename THead, typename... TTail>
  492. void AddMulti(THead& head, TTail&... tail) {
  493. Add(0, &head);
  494. AddMulti(tail...);
  495. }
  496. template <class T, typename = decltype(std::declval<T&>() & std::declval<IBinSaver&>())>
  497. static bool HasNonTrivialSerializer(ui32) {
  498. return true;
  499. }
  500. template <class T>
  501. static bool HasNonTrivialSerializer(...) {
  502. return sizeof(std::declval<IBinSaver*>()->Add(0, std::declval<T*>())) != 1;
  503. }
  504. public:
  505. IBinSaver(IBinaryStream& stream, bool _bRead, bool stableOutput = false)
  506. : bRead(_bRead)
  507. , File(_bRead, stream)
  508. , StableOutput(stableOutput)
  509. {
  510. }
  511. virtual ~IBinSaver();
  512. bool IsValid() const {
  513. return File.IsValid();
  514. }
  515. };
  516. // realisation of forward declared serialisation operator
  517. template <class TUserObj, class TRef>
  518. int TPtrBase<TUserObj, TRef>::operator&(IBinSaver& f) {
  519. f.DoPtr(this);
  520. return 0;
  521. }
  522. ////////////////////////////////////////////////////////////////////////////////////////////////////
  523. extern TClassFactory<IObjectBase>* pSaverClasses;
  524. void StartRegisterSaveload();
  525. template <class TReg>
  526. struct TRegisterSaveLoadType {
  527. TRegisterSaveLoadType(int num) {
  528. StartRegisterSaveload();
  529. pSaverClasses->RegisterType(num, TReg::NewSaveLoadNullItem, (TReg*)nullptr);
  530. }
  531. };
  532. #define Y_BINSAVER_REGISTER(name) \
  533. BASIC_REGISTER_CLASS(name) \
  534. static TRegisterSaveLoadType<name> init##name(MurmurHash<int>(#name, sizeof(#name)));
  535. #define REGISTER_SAVELOAD_CLASS(N, name) \
  536. BASIC_REGISTER_CLASS(name) \
  537. static TRegisterSaveLoadType<name> init##name##N(N);
  538. // using TObj/TRef on forward declared templ class will not work
  539. // but multiple registration with same id is allowed
  540. #define REGISTER_SAVELOAD_TEMPL1_CLASS(N, className, T) \
  541. static TRegisterSaveLoadType<className<T>> init##className##T##N(N);
  542. #define REGISTER_SAVELOAD_TEMPL2_CLASS(N, className, T1, T2) \
  543. typedef className<T1, T2> temp##className##T1##_##T2##temp; \
  544. static TRegisterSaveLoadType<className<T1, T2>> init##className##T1##_##T2##N(N);
  545. #define REGISTER_SAVELOAD_TEMPL3_CLASS(N, className, T1, T2, T3) \
  546. typedef className<T1, T2, T3> temp##className##T1##_##T2##_##T3##temp; \
  547. static TRegisterSaveLoadType<className<T1, T2, T3>> init##className##T1##_##T2##_##T3##N(N);
  548. #define REGISTER_SAVELOAD_NM_CLASS(N, nmspace, className) \
  549. BASIC_REGISTER_CLASS(nmspace::className) \
  550. static TRegisterSaveLoadType<nmspace::className> init_##nmspace##_##name##N(N);
  551. #define REGISTER_SAVELOAD_NM2_CLASS(N, nmspace1, nmspace2, className) \
  552. BASIC_REGISTER_CLASS(nmspace1::nmspace2::className) \
  553. static TRegisterSaveLoadType<nmspace1::nmspace2::className> init_##nmspace1##_##nmspace2##_##name##N(N);
  554. #define REGISTER_SAVELOAD_TEMPL1_NM_CLASS(N, nmspace, className, T) \
  555. typedef nmspace::className<T> temp_init##nmspace##className##T##temp; \
  556. BASIC_REGISTER_CLASS(nmspace::className<T>) \
  557. static TRegisterSaveLoadType<nmspace::className<T>> temp_init##nmspace##_##name##T##N(N);
  558. #define REGISTER_SAVELOAD_CLASS_NAME(N, cls, name) \
  559. BASIC_REGISTER_CLASS(cls) \
  560. static TRegisterSaveLoadType<cls> init##name##N(N);
  561. #define REGISTER_SAVELOAD_CLASS_NS_PREF(N, cls, ns, pref) \
  562. REGISTER_SAVELOAD_CLASS_NAME(N, ns ::cls, _##pref##_##cls)
  563. #define SAVELOAD(...) \
  564. int operator&(IBinSaver& f) { \
  565. f.AddMulti(__VA_ARGS__); \
  566. return 0; \
  567. } Y_SEMICOLON_GUARD
  568. #define SAVELOAD_OVERRIDE_WITHOUT_BASE(...) \
  569. int operator&(IBinSaver& f) override { \
  570. f.AddMulti(__VA_ARGS__); \
  571. return 0; \
  572. } Y_SEMICOLON_GUARD
  573. #define SAVELOAD_OVERRIDE(base, ...) \
  574. int operator&(IBinSaver& f) override { \
  575. base::operator&(f); \
  576. f.AddMulti(__VA_ARGS__); \
  577. return 0; \
  578. } Y_SEMICOLON_GUARD
  579. #define SAVELOAD_BASE(...) \
  580. int operator&(IBinSaver& f) { \
  581. TBase::operator&(f); \
  582. f.AddMulti(__VA_ARGS__); \
  583. return 0; \
  584. } Y_SEMICOLON_GUARD