compproto_ut.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. #include "huff.h"
  2. #include "metainfo.h"
  3. #include "bit.h"
  4. #include <util/generic/vector.h>
  5. #include <util/generic/map.h>
  6. #include <util/system/protect.h>
  7. #include <library/cpp/testing/unittest/registar.h>
  8. static ui64 gSeed = 42;
  9. static void FlushPseudoRandom() {
  10. gSeed = 42;
  11. }
  12. static ui32 PseudoRandom(ui32 max) {
  13. // stupid and non-threadsafe, but very predictable chaos generator
  14. gSeed += 1;
  15. gSeed *= 419;
  16. gSeed = gSeed ^ (ui64(max) << 17);
  17. return gSeed % max;
  18. }
  19. enum ECompMode {
  20. CM_SINGLEPASS,
  21. CM_TWOPASS
  22. };
  23. struct TTestParams {
  24. size_t DataSize;
  25. ui32 ValueArraySize;
  26. };
  27. template <typename X>
  28. void TestSaveLoadMeta(NCompProto::TMetaInfo<X>& src) {
  29. TStringStream ss;
  30. src.Save(ss);
  31. TString data = ss.Str();
  32. NCompProto::TMetaInfo<X> loadedMeta(data);
  33. ss = TStringStream();
  34. loadedMeta.Save(ss);
  35. UNIT_ASSERT_EQUAL(ss.Str(), data);
  36. }
  37. template <typename TDecompressor, template <typename, typename> class TSerialize>
  38. void TestWithParams(const TString& metainfo, const ECompMode mode, const TTestParams& params) {
  39. using namespace NCompProto;
  40. FlushPseudoRandom();
  41. TStringInput stream(metainfo);
  42. THolder<TMetaInfo<THuff>> meta;
  43. if (mode == CM_TWOPASS) {
  44. TMetaInfo<THist> hist(stream);
  45. TEmpty empty;
  46. TSerialize<THist, TEmpty>::Serialize(hist, empty, params);
  47. meta.Reset(new TMetaInfo<THuff>(hist, THistToHuff::Instance()));
  48. } else {
  49. meta.Reset(new TMetaInfo<THuff>(stream));
  50. }
  51. TestSaveLoadMeta(*meta.Get());
  52. TBitBuffer buffer;
  53. TSerialize<THuff, TBitBuffer>::Serialize(*meta, buffer, params);
  54. ui64 codedSize = buffer.Position;
  55. TMetaInfo<TTable> decompressor(*meta, THuffToTable::Instance());
  56. // verify that no memory read beyond buffer occurs
  57. const size_t byteSize = buffer.ByteLength();
  58. const size_t PAGESIZEX = 4096;
  59. const size_t busyPages = (byteSize + (PAGESIZEX - 1)) / PAGESIZEX;
  60. const size_t allPages = busyPages + 1;
  61. const size_t allocSize = (allPages + 1) * PAGESIZEX;
  62. TVector<ui8> readBuffer(allocSize);
  63. ui8* start = &readBuffer[0];
  64. ui8* pageStart = reinterpret_cast<ui8*>((size_t(start) + PAGESIZEX) & ~(PAGESIZEX - 1));
  65. // XX DATA DATA DATA DATA PROT
  66. // | | | | | pages
  67. // calculate dataStart so that data ends exactly at the page end
  68. ui8* dataStart = pageStart + busyPages * PAGESIZEX - byteSize;
  69. ui8* dataEnd = pageStart + busyPages * PAGESIZEX;
  70. ProtectMemory(dataEnd, PAGESIZEX, PM_NONE);
  71. // memory copying should be performed without any problems
  72. memcpy(dataStart, buffer.Out.data(), byteSize);
  73. ui64 position = 0;
  74. TMetaIterator<TDecompressor> instance;
  75. // we should not read beyond dataEnd here
  76. instance.Decompress(&decompressor, dataStart, position);
  77. const ui64 decodedSize = position;
  78. UNIT_ASSERT_EQUAL(codedSize, decodedSize);
  79. // unprotect memory
  80. ProtectMemory(dataEnd, PAGESIZEX, PM_READ | PM_WRITE | PM_EXEC);
  81. }
  82. template <typename TDecompressor, template <typename, typename> class TSerialize>
  83. void Test(const TString& metainfo, const ECompMode mode) {
  84. for (size_t ds = 3; ds < 42; ds += (3 + PseudoRandom(5))) {
  85. for (size_t vas = 5; vas < 42; vas += (4 + PseudoRandom(10))) {
  86. TTestParams params;
  87. params.DataSize = ds;
  88. params.ValueArraySize = vas;
  89. TestWithParams<TDecompressor, TSerialize>(metainfo, mode, params);
  90. }
  91. }
  92. }
  93. Y_UNIT_TEST_SUITE(CompProtoTestBasic) {
  94. using namespace NCompProto;
  95. const TString metainfo =
  96. "\n\
  97. repeated data id 0\n\
  98. scalar clicks id 0 default const 0\n\
  99. scalar shows id 1 default const 0\n\
  100. repeated regClicks id 2\n\
  101. scalar clicks id 0 default const 0\n\
  102. scalar shows id 1 default const 0\n\
  103. end\n\
  104. scalar extra id 31 default const 0\n\
  105. end\n";
  106. struct TRegInfo {
  107. ui32 Clicks;
  108. ui32 Shows;
  109. };
  110. struct TData {
  111. ui32 Clicks;
  112. ui32 Shows;
  113. ui32 Extra;
  114. TMap<ui32, TRegInfo> RegClicks;
  115. };
  116. TVector<TData> data;
  117. template <class TMeta, class TFunctor>
  118. struct TSerialize {
  119. static void Serialize(TMetaInfo<TMeta>& meta, TFunctor& functor, const TTestParams& params) {
  120. FlushPseudoRandom();
  121. meta.BeginSelf(functor);
  122. data.clear();
  123. data.resize(params.DataSize);
  124. for (ui32 i = 0; i < params.DataSize; ++i) {
  125. meta.BeginElement(i, functor);
  126. data[i].Clicks = PseudoRandom(16) + 100;
  127. data[i].Shows = PseudoRandom(500) * PseudoRandom(16);
  128. data[i].Extra = PseudoRandom(500) + (1UL << 31); // test also saving of big values
  129. meta.SetScalar(0, data[i].Clicks, functor);
  130. meta.SetScalar(1, data[i].Shows, functor);
  131. TMetaInfo<TMeta>& regClicks = meta.BeginRepeated(2, functor);
  132. for (ui32 j = 0; j < PseudoRandom(200); j += 1 + PseudoRandom(10)) {
  133. regClicks.BeginElement(j, functor);
  134. TRegInfo& r = data[i].RegClicks[j];
  135. r.Clicks = PseudoRandom(2);
  136. r.Shows = PseudoRandom(800) * PseudoRandom(8) + 56;
  137. regClicks.SetScalar(0, r.Clicks, functor);
  138. regClicks.SetScalar(1, r.Shows, functor);
  139. regClicks.EndElement(functor);
  140. }
  141. regClicks.EndRepeated(functor);
  142. meta.SetScalar(31, data[i].Extra, functor);
  143. meta.EndElement(functor);
  144. }
  145. meta.EndRepeated(functor);
  146. }
  147. };
  148. struct TMultiDecompressor: public TParentHold<TMultiDecompressor> {
  149. struct TRegClicks: public TParentHold<TRegClicks> {
  150. const TData* Data;
  151. const TRegInfo* Elem;
  152. TRegClicks()
  153. : Data(nullptr)
  154. , Elem(nullptr)
  155. {
  156. }
  157. void BeginSelf(ui32 /*count*/, ui32 /*id*/) {
  158. }
  159. void EndSelf() {
  160. }
  161. void BeginElement(ui32 element) {
  162. TMap<ui32, TRegInfo>::const_iterator it = Data->RegClicks.find(element);
  163. if (it == Data->RegClicks.end()) {
  164. UNIT_ASSERT(0);
  165. }
  166. Elem = &it->second;
  167. }
  168. void EndElement() {
  169. }
  170. void SetScalar(size_t index, ui32 val) {
  171. if (index == 0)
  172. UNIT_ASSERT_EQUAL(val, Elem->Clicks);
  173. if (index == 1)
  174. UNIT_ASSERT_EQUAL(val, Elem->Shows);
  175. }
  176. IDecompressor& GetDecompressor(size_t) {
  177. UNIT_ASSERT(0);
  178. return GetEmptyDecompressor();
  179. }
  180. };
  181. const TData* Elem;
  182. TMetaIterator<TRegClicks> RegClicks;
  183. void BeginSelf(ui32 /*count*/, ui32 /*id*/) {
  184. }
  185. void EndSelf() {
  186. }
  187. void BeginElement(ui32 element) {
  188. UNIT_ASSERT(element < data.size());
  189. Elem = &data[element];
  190. }
  191. void EndElement() {
  192. }
  193. void SetScalar(size_t index, ui32 val) {
  194. if (index == 0)
  195. UNIT_ASSERT_EQUAL(val, Elem->Clicks);
  196. if (index == 1)
  197. UNIT_ASSERT_EQUAL(val, Elem->Shows);
  198. if (index == 31)
  199. UNIT_ASSERT_EQUAL(val, Elem->Extra);
  200. }
  201. IDecompressor& GetDecompressor(size_t index) {
  202. if (index == 2) {
  203. RegClicks.Self.Data = Elem;
  204. return RegClicks;
  205. }
  206. UNIT_ASSERT(0);
  207. return GetEmptyDecompressor();
  208. }
  209. TMultiDecompressor()
  210. : Elem(nullptr)
  211. {
  212. }
  213. };
  214. struct TVerifyingDecompressor: public TParentHold<TVerifyingDecompressor> {
  215. enum EState {
  216. Startstop,
  217. OutDataElem,
  218. InDataElem,
  219. InRegClicks,
  220. };
  221. EState State;
  222. ui32 DataInd;
  223. TMap<ui32, TRegInfo>::iterator RegIter;
  224. TMetaIterator<TVerifyingDecompressor>& GetDecompressor(size_t index) {
  225. Y_UNUSED(index);
  226. return *Parent;
  227. }
  228. TVerifyingDecompressor()
  229. : State(Startstop)
  230. , DataInd(0)
  231. {
  232. }
  233. void BeginSelf(ui32 /*count*/, ui32 id) {
  234. switch (State) {
  235. case Startstop:
  236. UNIT_ASSERT_EQUAL(id, 0);
  237. State = OutDataElem;
  238. break;
  239. case OutDataElem:
  240. UNIT_ASSERT(0);
  241. case InDataElem:
  242. UNIT_ASSERT_EQUAL(id, 2);
  243. State = InRegClicks;
  244. RegIter = data[DataInd].RegClicks.begin();
  245. break;
  246. case InRegClicks:
  247. UNIT_ASSERT(0);
  248. default:
  249. UNIT_ASSERT(0);
  250. }
  251. }
  252. void EndSelf() {
  253. switch (State) {
  254. case Startstop:
  255. UNIT_ASSERT(0);
  256. case OutDataElem:
  257. State = Startstop;
  258. break;
  259. case InDataElem:
  260. UNIT_ASSERT(0);
  261. case InRegClicks:
  262. UNIT_ASSERT_EQUAL(RegIter, data[DataInd].RegClicks.end());
  263. State = InDataElem;
  264. break;
  265. default:
  266. UNIT_ASSERT(0);
  267. }
  268. }
  269. void BeginElement(ui32 element) {
  270. switch (State) {
  271. case Startstop:
  272. UNIT_ASSERT(0);
  273. case OutDataElem:
  274. UNIT_ASSERT(element < data.size());
  275. State = InDataElem;
  276. break;
  277. case InDataElem:
  278. UNIT_ASSERT(0);
  279. case InRegClicks:
  280. UNIT_ASSERT_EQUAL(element, RegIter->first);
  281. break;
  282. }
  283. }
  284. void EndElement() {
  285. switch (State) {
  286. case Startstop:
  287. UNIT_ASSERT(0);
  288. case OutDataElem:
  289. UNIT_ASSERT(0);
  290. case InDataElem:
  291. State = OutDataElem;
  292. ++DataInd;
  293. break;
  294. case InRegClicks:
  295. ++RegIter;
  296. break;
  297. }
  298. }
  299. void SetScalar(size_t index, ui32 val) {
  300. switch (State) {
  301. case OutDataElem:
  302. UNIT_ASSERT(0);
  303. case InDataElem:
  304. if (index == 0)
  305. UNIT_ASSERT_EQUAL(val, data[DataInd].Clicks);
  306. if (index == 1)
  307. UNIT_ASSERT_EQUAL(val, data[DataInd].Shows);
  308. if (index == 31)
  309. UNIT_ASSERT_EQUAL(val, data[DataInd].Extra);
  310. break;
  311. case InRegClicks:
  312. if (index == 0)
  313. UNIT_ASSERT_EQUAL(val, RegIter->second.Clicks);
  314. if (index == 1)
  315. UNIT_ASSERT_EQUAL(val, RegIter->second.Shows);
  316. break;
  317. default:
  318. UNIT_ASSERT(0);
  319. }
  320. }
  321. };
  322. Y_UNIT_TEST(VerifyDecompression) {
  323. Test<TVerifyingDecompressor, TSerialize>(metainfo, CM_SINGLEPASS);
  324. }
  325. Y_UNIT_TEST(VerifyHistDecompression) {
  326. Test<TVerifyingDecompressor, TSerialize>(metainfo, CM_TWOPASS);
  327. }
  328. Y_UNIT_TEST(VerifyDecompressionMulti) {
  329. Test<TMultiDecompressor, TSerialize>(metainfo, CM_SINGLEPASS);
  330. }
  331. Y_UNIT_TEST(VerifyHistDecompressionMulti) {
  332. Test<TMultiDecompressor, TSerialize>(metainfo, CM_TWOPASS);
  333. }
  334. }
  335. Y_UNIT_TEST_SUITE(CompProtoTestExtended) {
  336. using namespace NCompProto;
  337. const TString metainfo =
  338. "\n\
  339. repeated data id 0\n\
  340. repeated second id 3\n\
  341. scalar inner2 id 0 default const 0\n\
  342. end\n\
  343. repeated first id 2\n\
  344. scalar inner id 0 default const 0\n\
  345. end\n\
  346. end\n";
  347. TVector<std::pair<TVector<ui32>, TVector<ui32>>> data;
  348. template <class TMeta, class TFunctor>
  349. struct TSerialize {
  350. static void Serialize(TMetaInfo<TMeta>& meta, TFunctor& functor, const TTestParams& params) {
  351. FlushPseudoRandom();
  352. meta.BeginSelf(functor);
  353. data.clear();
  354. data.resize(params.DataSize);
  355. for (size_t i = 0; i < params.DataSize; ++i) {
  356. meta.BeginElement(i, functor);
  357. TMetaInfo<TMeta>& first = meta.BeginRepeated(2, functor);
  358. data[i].first.resize(params.ValueArraySize);
  359. for (ui32 j = 0; j < params.ValueArraySize; j++) {
  360. first.BeginElement(j, functor);
  361. ui32 val = PseudoRandom(42 * 42 * 42);
  362. first.SetScalar(0, val, functor);
  363. data[i].first[j] = val;
  364. first.EndElement(functor);
  365. }
  366. first.EndRepeated(functor);
  367. TMetaInfo<TMeta>& second = meta.BeginRepeated(3, functor);
  368. data[i].second.resize(params.ValueArraySize);
  369. for (ui32 j = 0; j < params.ValueArraySize; j++) {
  370. second.BeginElement(j, functor);
  371. ui32 val = PseudoRandom(42 * 42 * 42);
  372. second.SetScalar(0, val, functor);
  373. data[i].second[j] = val;
  374. second.EndElement(functor);
  375. }
  376. second.EndRepeated(functor);
  377. meta.EndElement(functor);
  378. }
  379. meta.EndRepeated(functor);
  380. }
  381. };
  382. struct TVerifyingDecompressor: public TParentHold<TVerifyingDecompressor> {
  383. enum EState {
  384. Startstop,
  385. OutDataElem,
  386. InDataElemBeforeSecond,
  387. InDataElemSecond,
  388. InFirst,
  389. InSecond,
  390. };
  391. EState State;
  392. ui32 DataInd;
  393. ui32 ArrayInd;
  394. TVerifyingDecompressor()
  395. : State(Startstop)
  396. , DataInd(0)
  397. , ArrayInd(0)
  398. {
  399. }
  400. TMetaIterator<TVerifyingDecompressor>& GetDecompressor(size_t index) {
  401. Y_UNUSED(index);
  402. return *Parent;
  403. }
  404. void BeginSelf(ui32 /*count*/, ui32 id) {
  405. switch (State) {
  406. case Startstop:
  407. UNIT_ASSERT_EQUAL(id, 0);
  408. State = OutDataElem;
  409. break;
  410. case InDataElemBeforeSecond:
  411. UNIT_ASSERT_EQUAL(id, 2);
  412. State = InFirst;
  413. ArrayInd = 0;
  414. break;
  415. case InDataElemSecond:
  416. UNIT_ASSERT_EQUAL(id, 3);
  417. State = InSecond;
  418. ArrayInd = 0;
  419. break;
  420. default:
  421. UNIT_ASSERT(0);
  422. }
  423. }
  424. void EndSelf() {
  425. switch (State) {
  426. case OutDataElem:
  427. State = Startstop;
  428. break;
  429. case InFirst:
  430. State = InDataElemSecond;
  431. break;
  432. case InSecond:
  433. State = InDataElemSecond;
  434. break;
  435. default:
  436. UNIT_ASSERT(0);
  437. }
  438. }
  439. void BeginElement(ui32 element) {
  440. switch (State) {
  441. case OutDataElem:
  442. UNIT_ASSERT(element < data.size());
  443. State = InDataElemBeforeSecond;
  444. break;
  445. case InFirst:
  446. UNIT_ASSERT(element < data[DataInd].first.size());
  447. break;
  448. case InSecond:
  449. UNIT_ASSERT(element < data[DataInd].second.size());
  450. break;
  451. default:
  452. Cerr << (ui32)State << Endl;
  453. UNIT_ASSERT(0);
  454. }
  455. }
  456. void EndElement() {
  457. switch (State) {
  458. case InFirst:
  459. case InSecond:
  460. ++ArrayInd;
  461. break;
  462. case InDataElemSecond:
  463. ++DataInd;
  464. State = OutDataElem;
  465. break;
  466. default:
  467. Cerr << (ui32)State << Endl;
  468. UNIT_ASSERT(0);
  469. }
  470. }
  471. void SetScalar(size_t index, ui32 val) {
  472. UNIT_ASSERT_EQUAL(index, 0);
  473. switch (State) {
  474. case InFirst:
  475. UNIT_ASSERT_EQUAL(val, data[DataInd].first[ArrayInd]);
  476. break;
  477. case InSecond:
  478. UNIT_ASSERT_EQUAL(val, data[DataInd].second[ArrayInd]);
  479. break;
  480. default:
  481. UNIT_ASSERT(0);
  482. }
  483. }
  484. };
  485. Y_UNIT_TEST(VerifyDecompression) {
  486. Test<TVerifyingDecompressor, TSerialize>(metainfo, CM_SINGLEPASS);
  487. }
  488. Y_UNIT_TEST(VerifyHistDecompression) {
  489. Test<TVerifyingDecompressor, TSerialize>(metainfo, CM_TWOPASS);
  490. }
  491. }