rtldynfield.cpp 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "platform.h"
  14. #include <math.h>
  15. #include <stdio.h>
  16. #include "jmisc.hpp"
  17. #include "jlib.hpp"
  18. #include "eclhelper.hpp"
  19. #include "eclrtl_imp.hpp"
  20. #include "rtldynfield.hpp"
  21. #include "rtlrecord.hpp"
  22. #include "rtlembed.hpp"
  23. //#define TRACE_TRANSLATION
  24. #define VALIDATE_TYPEINFO_HASHES
  25. #define RTLTYPEINFO_FORMAT_1 80 // In case we ever want to support more than one format
  26. //---------------------------------------------------------------------------------------------------------------------
  27. const RtlTypeInfo *FieldTypeInfoStruct::createRtlTypeInfo(IThorIndexCallback *_callback) const
  28. {
  29. const RtlTypeInfo *ret = nullptr;
  30. switch (fieldType & RFTMkind)
  31. {
  32. case type_boolean:
  33. ret = new RtlBoolTypeInfo(fieldType, length);
  34. break;
  35. case type_keyedint:
  36. ret = new RtlKeyedIntTypeInfo(fieldType, length, childType);
  37. break;
  38. case type_blob: // MORE - will need its own type!
  39. case type_int:
  40. ret = new RtlIntTypeInfo(fieldType, length);
  41. break;
  42. case type_filepos:
  43. ret = new RtlFileposTypeInfo(fieldType, length, childType, _callback);
  44. break;
  45. case type_real:
  46. ret = new RtlRealTypeInfo(fieldType, length);
  47. break;
  48. case type_decimal:
  49. ret = new RtlDecimalTypeInfo(fieldType, length);
  50. break;
  51. case type_string:
  52. ret = new RtlStringTypeInfo(fieldType, length);
  53. break;
  54. case type_bitfield:
  55. ret = new RtlBitfieldTypeInfo(fieldType, length);
  56. break;
  57. case type_varstring:
  58. ret = new RtlVarStringTypeInfo(fieldType, length);
  59. break;
  60. case type_data:
  61. ret = new RtlDataTypeInfo(fieldType, length);
  62. break;
  63. case type_table:
  64. assert(childType);
  65. ret = new RtlDatasetTypeInfo(fieldType, length, childType);
  66. break;
  67. case type_set:
  68. assert(childType);
  69. ret = new RtlSetTypeInfo(fieldType, length, childType);
  70. break;
  71. case type_row:
  72. assert(childType);
  73. ret = new RtlRowTypeInfo(fieldType, length, childType);
  74. break;
  75. case type_swapint:
  76. ret = new RtlSwapIntTypeInfo(fieldType, length);
  77. break;
  78. case type_packedint:
  79. ret = new RtlPackedIntTypeInfo(fieldType, length);
  80. break;
  81. case type_qstring:
  82. ret = new RtlQStringTypeInfo(fieldType, length);
  83. break;
  84. case type_unicode:
  85. ret = new RtlUnicodeTypeInfo(fieldType, length, locale);
  86. break;
  87. case type_varunicode:
  88. ret = new RtlVarUnicodeTypeInfo(fieldType, length, locale);
  89. break;
  90. case type_utf8:
  91. ret = new RtlUtf8TypeInfo(fieldType, length, locale);
  92. break;
  93. case type_record:
  94. ret = new RtlRecordTypeInfo(fieldType, length, fieldsArray);
  95. break;
  96. case type_ifblock:
  97. ret = new RtlDynamicIfBlockTypeInfo(fieldType, length, fieldsArray);
  98. break;
  99. default:
  100. throwUnexpected();
  101. }
  102. return ret;
  103. };
  104. typedef MapBetween<const RtlTypeInfo *, const RtlTypeInfo *, StringAttr, const char *> TypeNameMap;
  105. typedef MapBetween<const RtlTypeInfo *, const RtlTypeInfo *, unsigned, unsigned> TypeNumMap;
  106. /**
  107. * class CRtlFieldTypeSerializer
  108. *
  109. * Serializer class for creating json representation of a RtlTypeInfo structure.
  110. *
  111. */
  112. class CRtlFieldTypeSerializer
  113. {
  114. public:
  115. /**
  116. * Serialize a RtlTypeInfo structure to JSON
  117. *
  118. * @param out Buffer for resulting serialized string
  119. * @param type RtlTypeInfo structure to be serialized
  120. * @return Referenced to supplied buffer
  121. */
  122. static StringBuffer &serialize(StringBuffer &out, const RtlTypeInfo *type)
  123. {
  124. CRtlFieldTypeSerializer s(out, type);
  125. s.doSerialize();
  126. return out;
  127. }
  128. private:
  129. CRtlFieldTypeSerializer(StringBuffer &_out, const RtlTypeInfo *_base)
  130. : json(_out), base(_base)
  131. {
  132. }
  133. void doSerialize()
  134. {
  135. json.append("{");
  136. serializeType(base);
  137. json.append("\n}");
  138. }
  139. void serializeType(const RtlTypeInfo *type)
  140. {
  141. if (!serialized(type))
  142. {
  143. // Make sure all child types are serialized first
  144. const RtlTypeInfo *child = type->queryChildType();
  145. if (child)
  146. serializeType(child);
  147. const RtlFieldInfo * const * fields = type->queryFields();
  148. if (fields)
  149. {
  150. for (;;)
  151. {
  152. const RtlFieldInfo * child = *fields;
  153. if (!child)
  154. break;
  155. serializeType(child->type);
  156. fields++;
  157. }
  158. }
  159. // Now serialize this one
  160. if (type != base)
  161. {
  162. VStringBuffer newName("ty%d", ++nextTypeName);
  163. types.setValue(type, newName.str());
  164. startField(newName.str());
  165. serializeMe(type);
  166. closeCurly();
  167. }
  168. else
  169. serializeMe(type);
  170. }
  171. }
  172. void serializeMe(const RtlTypeInfo *type)
  173. {
  174. if (!type->canSerialize())
  175. throw makeStringException(MSGAUD_user, 1000, "This type structure cannot be serialized");
  176. addPropHex("fieldType", type->fieldType);
  177. addProp("length", type->length);
  178. addPropNonEmpty("locale", type->queryLocale());
  179. const RtlTypeInfo *child = type->queryChildType();
  180. if (child)
  181. addPropType("child", child);
  182. const RtlFieldInfo * const * fields = type->queryFields();
  183. if (fields)
  184. {
  185. startFields();
  186. for (;;)
  187. {
  188. const RtlFieldInfo * child = *fields;
  189. if (!child)
  190. break;
  191. newline();
  192. openCurly();
  193. addProp("name", child->name);
  194. addPropType("type", child->type);
  195. addProp("xpath", child->xpath);
  196. if (child->flags)
  197. addPropHex("flags", child->flags);
  198. // initializer is tricky - it's not (in general) a null-terminated string but the actual length is not easily available
  199. if (child->initializer)
  200. {
  201. addProp("init", child->type->size(child->initializer, nullptr), child->initializer);
  202. }
  203. closeCurly();
  204. fields++;
  205. }
  206. endFields();
  207. }
  208. }
  209. bool serialized(const RtlTypeInfo *type)
  210. {
  211. return types.find(type) != nullptr;
  212. }
  213. void startField(const char *name)
  214. {
  215. newline().appendf("\"%s\": ", name);
  216. openCurly();
  217. }
  218. void addProp(const char *propName, const char *propVal)
  219. {
  220. if (propVal)
  221. {
  222. newline();
  223. encodeJSON(json.append("\""), propName).append("\": ");
  224. encodeJSON(json.append("\""), propVal).append("\"");
  225. }
  226. }
  227. void addProp(const char *propName, size32_t propLen, const byte *propVal)
  228. {
  229. if (propVal)
  230. {
  231. newline();
  232. encodeJSON(json.append("\""), propName).append("\": \"");
  233. JBASE64_Encode(propVal, propLen, json, false);
  234. json.append("\"");
  235. }
  236. }
  237. void addPropNonEmpty(const char *propName, const char *propVal)
  238. {
  239. if (propVal && *propVal)
  240. addProp(propName, propVal);
  241. }
  242. void addProp(const char *propName, unsigned propVal)
  243. {
  244. newline().appendf("\"%s\": %u", propName, propVal);
  245. }
  246. void addPropHex(const char *propName, unsigned propVal)
  247. {
  248. newline().appendf("\"%s\": %u", propName, propVal); // Nice idea but json does not support hex constants :(
  249. }
  250. void addPropType(const char *propName, const RtlTypeInfo *type)
  251. {
  252. addProp(propName, queryTypeName(type));
  253. }
  254. const char *queryTypeName(const RtlTypeInfo *type)
  255. {
  256. StringAttr *typeName = types.getValue(type);
  257. assertex(typeName);
  258. return typeName->get();
  259. }
  260. void startFields()
  261. {
  262. newline().appendf("\"fields\": ");
  263. openCurly('[');
  264. }
  265. void endFields()
  266. {
  267. closeCurly(']');
  268. }
  269. StringBuffer &newline()
  270. {
  271. if (commaPending)
  272. json.append(',');
  273. json.appendf("\n%*s", indent, "");
  274. commaPending = true;
  275. return json;
  276. }
  277. void closeCurly(char brace = '}')
  278. {
  279. indent--;
  280. json.appendf("\n%*s%c", indent, "", brace);
  281. commaPending = true;
  282. }
  283. void openCurly(char brace = '{')
  284. {
  285. json.append(brace);
  286. indent++;
  287. commaPending = false;
  288. }
  289. TypeNameMap types;
  290. StringBuffer &json;
  291. const RtlTypeInfo *base = nullptr;
  292. unsigned indent = 1;
  293. unsigned nextTypeName = 0;
  294. bool commaPending = false;
  295. };
  296. class IndexBiasTranslator
  297. {
  298. public:
  299. IndexBiasTranslator(const RtlTypeInfo *type)
  300. {
  301. translatedType = type; // Assume no translation needed until proven otherwise
  302. if (type->getType() != type_record)
  303. return;
  304. const RtlFieldInfo * const * fields = type->queryFields();
  305. if (!fields)
  306. return;
  307. unsigned numFields;
  308. needsTranslation = false;
  309. for (numFields=0;;numFields++)
  310. {
  311. const RtlFieldInfo * child = fields[numFields];
  312. if (!child)
  313. break;
  314. switch (child->type->getType())
  315. {
  316. #if __BYTE_ORDER == __LITTLE_ENDIAN
  317. case type_swapint:
  318. if (!child->type->isUnsigned())
  319. needsTranslation = true;
  320. break;
  321. case type_int:
  322. needsTranslation = true;
  323. break;
  324. #else
  325. case type_int:
  326. if (!child->type->isUnsigned())
  327. needsTranlsation = true;
  328. break;
  329. case type_swapint:
  330. needsTranslation = true;
  331. break;
  332. #endif
  333. }
  334. }
  335. if (!needsTranslation && numFields > 1)
  336. {
  337. // Check if need last field translating to a type_filepos
  338. switch(fields[numFields]->type->getType())
  339. {
  340. case type_int:
  341. case type_swapint:
  342. case type_packedint:
  343. case type_bitfield:
  344. needsTranslation = true;
  345. }
  346. }
  347. if (needsTranslation)
  348. {
  349. translated = new bool[numFields];
  350. RtlFieldInfo * * newFields = new RtlFieldInfo * [numFields+1];
  351. newFields[numFields] = nullptr;
  352. for (unsigned idx = 0; idx < numFields; idx++)
  353. {
  354. newFields[idx] = new RtlFieldInfo(*fields[idx]);
  355. const RtlTypeInfo *newType = createBiasType(fields[idx]->type, idx > 1 && idx == numFields-1);
  356. // MORE - Is it an issue if we don't common these up?
  357. if (newType)
  358. {
  359. newFields[idx]->type = newType;
  360. translated[idx] = true;
  361. }
  362. else
  363. translated[idx] = false;
  364. }
  365. translatedType = new RtlRecordTypeInfo(type->fieldType, type->length, newFields);
  366. }
  367. }
  368. ~IndexBiasTranslator()
  369. {
  370. if (needsTranslation)
  371. {
  372. const RtlFieldInfo * const * fields = translatedType->queryFields();
  373. for (unsigned idx = 0;;idx++)
  374. {
  375. const RtlFieldInfo * child = fields[idx];
  376. if (!child)
  377. break;
  378. if (translated[idx])
  379. child->type->doDelete();
  380. delete child;
  381. }
  382. delete [] fields;
  383. translatedType->doDelete();
  384. delete [] translated;
  385. }
  386. }
  387. const RtlTypeInfo *queryTranslatedType()
  388. {
  389. return translatedType;
  390. }
  391. private:
  392. static const RtlTypeInfo *createBiasType(const RtlTypeInfo *origType, bool isLastField)
  393. {
  394. auto type = origType->getType();
  395. if (type==type_int || type==type_swapint)
  396. {
  397. unsigned flags = origType->fieldType & ~RFTMkind;
  398. unsigned length = origType->length;
  399. if (isLastField)
  400. return new RtlFileposTypeInfo(type_filepos | flags, length, origType, nullptr);
  401. #if __BYTE_ORDER == __LITTLE_ENDIAN
  402. else if (type == type_int || origType->isSigned())
  403. #else
  404. else if (type == type_swapint || origType->isSigned()) // MORE - this may not be right if compiler machine endianness does not match this machine
  405. #endif
  406. return new RtlKeyedIntTypeInfo(type_keyedint | flags, length, origType);
  407. }
  408. return nullptr;
  409. }
  410. const RtlTypeInfo *translatedType = nullptr;
  411. bool needsTranslation = false;
  412. bool *translated = nullptr;
  413. };
  414. class CRtlFieldTypeBinSerializer
  415. {
  416. public:
  417. /**
  418. * Serialize a RtlTypeInfo structure to binary
  419. *
  420. * @param out Buffer for resulting serialized string
  421. * @param type RtlTypeInfo structure to be serialized
  422. * @return Referenced to supplied buffer
  423. */
  424. static MemoryBuffer &serialize(MemoryBuffer &out, const RtlTypeInfo *type, bool applyBias)
  425. {
  426. int oldEnd = out.setEndian(__LITTLE_ENDIAN);
  427. CRtlFieldTypeBinSerializer s(out);
  428. byte format = RTLTYPEINFO_FORMAT_1;
  429. out.append(format);
  430. DelayedMarker<hash64_t> hash(out);
  431. DelayedSizeMarker size(out);
  432. size32_t pos = out.length();
  433. if (applyBias)
  434. {
  435. IndexBiasTranslator translator(type);
  436. s.serializeType(translator.queryTranslatedType());
  437. }
  438. else
  439. s.serializeType(type);
  440. size.write();
  441. hash.write(rtlHash64Data(size.size(), out.toByteArray()+pos, 0));
  442. out.setEndian(oldEnd);
  443. return out;
  444. }
  445. private:
  446. CRtlFieldTypeBinSerializer(MemoryBuffer &_out)
  447. : out(_out)
  448. {
  449. }
  450. void serializeType(const RtlTypeInfo *type)
  451. {
  452. if (!serialized(type))
  453. {
  454. // Make sure all child types are serialized first
  455. const RtlTypeInfo *child = type->queryChildType();
  456. if (child)
  457. serializeType(child);
  458. const RtlFieldInfo * const * fields = type->queryFields();
  459. if (fields)
  460. {
  461. for (unsigned idx = 0;;idx++)
  462. {
  463. const RtlFieldInfo * child = fields[idx];
  464. if (!child)
  465. break;
  466. serializeType(child->type);
  467. }
  468. }
  469. // Now serialize this one
  470. types.setValue(type, nextTypeNum++);
  471. serializeMe(type);
  472. }
  473. }
  474. void serializeMe(const RtlTypeInfo *type)
  475. {
  476. if (!type->canSerialize())
  477. throw makeStringException(MSGAUD_user, 1000, "This type structure cannot be serialized");
  478. unsigned fieldType = type->fieldType;
  479. const char *locale = type->queryLocale();
  480. if (locale && *locale)
  481. fieldType |= RFTMhasLocale;
  482. const RtlTypeInfo *child = type->queryChildType();
  483. if (child)
  484. fieldType |= RFTMhasChildType;
  485. const RtlFieldInfo * const * fields = type->queryFields();
  486. if (fields)
  487. fieldType |= RFTMhasFields;
  488. out.append(fieldType);
  489. out.appendPacked(type->length);
  490. if (fieldType & RFTMhasLocale)
  491. out.append(locale);
  492. if (child)
  493. out.appendPacked(queryTypeIdx(child));
  494. if (fields)
  495. {
  496. unsigned count = countFields(fields);
  497. out.appendPacked(count);
  498. for (;;)
  499. {
  500. const RtlFieldInfo * child = *fields;
  501. if (!child)
  502. break;
  503. out.append(child->name);
  504. out.appendPacked(queryTypeIdx(child->type));
  505. unsigned flags = child->flags;
  506. if (child->xpath)
  507. flags |= RFTMhasXpath;
  508. if (child->initializer)
  509. flags |= RFTMhasInitializer;
  510. out.append(flags);
  511. if (child->xpath)
  512. out.append(child->xpath);
  513. // initializer is tricky - it's not (in general) a null-terminated string but the actual length is not easily available
  514. if (child->initializer)
  515. {
  516. unsigned initLength = child->type->size(child->initializer, nullptr);
  517. out.appendPacked(initLength).append(initLength, child->initializer);
  518. }
  519. fields++;
  520. }
  521. }
  522. }
  523. bool serialized(const RtlTypeInfo *type)
  524. {
  525. return types.find(type) != nullptr;
  526. }
  527. unsigned queryTypeIdx(const RtlTypeInfo *type)
  528. {
  529. unsigned *typeNum = types.getValue(type);
  530. assertex(typeNum);
  531. return *typeNum;
  532. }
  533. TypeNumMap types;
  534. MemoryBuffer &out;
  535. unsigned nextTypeNum = 0;
  536. };
  537. /**
  538. * class CRtlFieldTypeDeserializer
  539. *
  540. * Deserializer class for creating a RtlTypeInfo structure from json representation.
  541. *
  542. * Note that the resulting RtlTypeInfo structures are owned by this object and will be
  543. * destroyed when this object is destroyed.
  544. *
  545. */
  546. class CRtlFieldTypeDeserializer : public CInterfaceOf<IRtlFieldTypeDeserializer>
  547. {
  548. public:
  549. /**
  550. * CRtlFieldTypeDeserializer constructor
  551. *
  552. * @param _callback Supplies a callback to be used for blobs/filepositions.
  553. */
  554. CRtlFieldTypeDeserializer(IThorIndexCallback *_callback)
  555. : callback(_callback)
  556. {
  557. }
  558. /**
  559. * CRtlFieldTypeDeserializer destructor
  560. * <p>
  561. * Releases all RtlTypeInfo and related structures created by this deserializer
  562. */
  563. ~CRtlFieldTypeDeserializer()
  564. {
  565. // Need some care - all the RtlTypeInfo objects I created need to be destroyed, together with anything else I had to create
  566. // Strings (other than the init strings) are preserved in the AtomTable
  567. HashIterator allTypes(types);
  568. ForEach(allTypes)
  569. {
  570. const RtlTypeInfo **type = types.mapToValue(&allTypes.query());
  571. cleanupType(*type);
  572. }
  573. cleanupType(base);
  574. }
  575. /**
  576. * Obtain the deserialized type information
  577. * <p>
  578. * Note that the RtlTypeInfo objects are not link-counted, so the lifetime of these objects
  579. * is determined by the lifetime of the deserializer. They will be released once the deserializer
  580. * that created them is deleted.
  581. * <p>
  582. * Do not call more than once.
  583. *
  584. * @param _json JSON text to be deserialized, as created by CRtlFieldTypeSerializer
  585. * @return Deserialized type object
  586. */
  587. virtual const RtlTypeInfo *deserialize(const char *json) override
  588. {
  589. assertex(!base);
  590. Owned<IPropertyTree> jsonTree = createPTreeFromJSONString(json);
  591. base = deserializeType(jsonTree, jsonTree);
  592. return base;
  593. }
  594. /**
  595. * Obtain the deserialized type information
  596. * <p>
  597. * Note that the RtlTypeInfo objects are not link-counted, so the lifetime of these objects
  598. * is determined by the lifetime of the deserializer. They will be released once the deserializer
  599. * that created them is deleted.
  600. * <p>
  601. * Do not call more than once.
  602. *
  603. * @param _jsonTree JSON property tree to be deserialized, as created by CRtlFieldTypeSerializer
  604. * @return Deserialized type object
  605. */
  606. virtual const RtlTypeInfo *deserialize(IPropertyTree &jsonTree) override
  607. {
  608. assertex(!base);
  609. base = deserializeType(&jsonTree, &jsonTree);
  610. return base;
  611. }
  612. /**
  613. * Obtain the deserialized type information
  614. * <p>
  615. * Note that the RtlTypeInfo objects are not link-counted, so the lifetime of these objects
  616. * is determined by the lifetime of the deserializer. They will be released once the deserializer
  617. * that created them is deleted.
  618. * <p>
  619. * Do not call more than once.
  620. *
  621. * @param buf Binary serialized typeinfo to be deserialized, as created by CRtlFieldTypeSerializer
  622. * @return Deserialized type object
  623. */
  624. virtual const RtlTypeInfo *deserialize(MemoryBuffer &buf) override
  625. {
  626. assertex(!base);
  627. unsigned nextTypeNum = 0;
  628. int oldEndian = buf.setEndian(__LITTLE_ENDIAN);
  629. try
  630. {
  631. byte format;
  632. buf.read(format);
  633. if (format != RTLTYPEINFO_FORMAT_1)
  634. throw MakeStringException(0, "Invalid type info (%d) in CRtlFieldTypeDeserializer::deserialize", format);
  635. hash64_t hash;
  636. buf.read(hash);
  637. size32_t size;
  638. buf.read(size);
  639. #ifdef VALIDATE_TYPEINFO_HASHES
  640. hash64_t expected = rtlHash64Data(size, buf.readDirect(0), 0);
  641. if (expected != hash)
  642. throw MakeStringException(0, "Invalid type info hash in CRtlFieldTypeDeserializer::deserialize");
  643. #endif
  644. size32_t endpos = buf.getPos() + size;
  645. while (buf.getPos() < endpos)
  646. {
  647. if (base)
  648. {
  649. addType(base, nextTypeNum++);
  650. base = nullptr; // in case of exceptions...
  651. }
  652. base = deserializeType(buf);
  653. }
  654. if (buf.getPos()!=endpos)
  655. throw MakeStringException(0, "Invalid type info (incorrect size data) in CRtlFieldTypeDeserializer::deserialize");
  656. buf.setEndian(oldEndian);
  657. return base;
  658. }
  659. catch(...)
  660. {
  661. buf.setEndian(oldEndian);
  662. throw;
  663. }
  664. }
  665. virtual const RtlTypeInfo *addType(FieldTypeInfoStruct &info, const ITypeInfo *type) override
  666. {
  667. VStringBuffer name("%p", type);
  668. const RtlTypeInfo ** found = types.getValue(name);
  669. if (found)
  670. return *found;
  671. info.locale = keep(info.locale);
  672. const RtlTypeInfo * ret = info.createRtlTypeInfo(callback);
  673. types.setValue(name, ret);
  674. return ret;
  675. }
  676. virtual const RtlTypeInfo *lookupType(const ITypeInfo *type) const override
  677. {
  678. VStringBuffer name("%p", type);
  679. const RtlTypeInfo ** found = types.getValue(name);
  680. if (found)
  681. return *found;
  682. return nullptr;
  683. }
  684. virtual const RtlFieldInfo *addFieldInfo(const char *fieldName, const char *xpath, const RtlTypeInfo *type, unsigned flags, const char *init) override
  685. {
  686. // MORE - we could hang onto this for cleanup, rather than assuming that we keep it via a later addType() call?
  687. return new RtlFieldStrInfo(keep(fieldName), keep(xpath), type, flags, init);
  688. }
  689. private:
  690. KeptAtomTable atoms; // Used to ensure proper lifetime of strings used in type structures
  691. MapStringTo<const RtlTypeInfo *> types; // Ensures structures only generated once
  692. const RtlTypeInfo *base = nullptr; // Holds the resulting type
  693. IThorIndexCallback *callback = nullptr;
  694. void cleanupType(const RtlTypeInfo *type)
  695. {
  696. if (type)
  697. {
  698. // Releases all memory for a single RtlTypeInfo object
  699. const RtlFieldInfo * const * fields = type->queryFields();
  700. if (fields)
  701. {
  702. const RtlFieldInfo * const * cur = fields;
  703. for (;;)
  704. {
  705. const RtlFieldInfo * child = *cur;
  706. if (!child)
  707. break;
  708. // We don't need to delete other strings - they are owned by atom table.
  709. // But the initializer is decoded and thus owned by me
  710. delete child->initializer;
  711. delete child;
  712. cur++;
  713. }
  714. delete [] fields;
  715. }
  716. type->doDelete();
  717. }
  718. }
  719. const RtlTypeInfo *lookupType(const char *name, IPropertyTree *all)
  720. {
  721. const RtlTypeInfo ** found = types.getValue(name);
  722. if (found)
  723. return *found;
  724. const RtlTypeInfo *type = deserializeType(all->queryPropTree(name), all);
  725. types.setValue(name, type);
  726. return type;
  727. }
  728. const RtlTypeInfo *lookupType(unsigned idx)
  729. {
  730. // Could keep an expanding array of types instead - but the hash table is already there for json support...
  731. VStringBuffer key("%u", idx);
  732. const RtlTypeInfo ** found = types.getValue(key);
  733. if (found)
  734. return *found;
  735. throw makeStringException(-1, "Invalid serialized type information");
  736. }
  737. void addType(const RtlTypeInfo *type, unsigned idx)
  738. {
  739. VStringBuffer key("%u", idx);
  740. assert(types.getValue(key)==nullptr);
  741. types.setValue(key, type);
  742. }
  743. const char *keep(const char *string)
  744. {
  745. if (string)
  746. return str(atoms.addAtom(string));
  747. else
  748. return nullptr;
  749. }
  750. const RtlTypeInfo *deserializeType(IPropertyTree *type, IPropertyTree *all)
  751. {
  752. FieldTypeInfoStruct info;
  753. info.fieldType = type->getPropInt("fieldType");
  754. info.length = type->getPropInt("length");
  755. info.locale = keep(type->queryProp("locale"));
  756. const char *child = type->queryProp("child");
  757. if (child)
  758. info.childType = lookupType(child, all);
  759. if ((info.fieldType & RFTMkind) == type_record)
  760. {
  761. unsigned numFields = type->getCount("fields");
  762. info.fieldsArray = new const RtlFieldInfo * [numFields+1];
  763. info.fieldsArray[numFields] = nullptr;
  764. Owned<IPropertyTreeIterator> fields = type->getElements("fields");
  765. unsigned n = 0;
  766. ForEach(*fields)
  767. {
  768. IPropertyTree &field = fields->query();
  769. const char *fieldTypeName = field.queryProp("type");
  770. const char *fieldName = keep(field.queryProp("name"));
  771. const char *fieldXpath = keep(field.queryProp("xpath"));
  772. unsigned flags = field.getPropInt("flags");
  773. const char *fieldInit = field.queryProp("init");
  774. if (fieldInit)
  775. {
  776. StringBuffer decoded;
  777. JBASE64_Decode(fieldInit, decoded);
  778. fieldInit = decoded.detach(); // NOTE - this gets freed in cleanupType()
  779. }
  780. info.fieldsArray[n] = new RtlFieldStrInfo(fieldName, fieldXpath, lookupType(fieldTypeName, all), flags, fieldInit);
  781. n++;
  782. }
  783. }
  784. return info.createRtlTypeInfo(callback);
  785. }
  786. const RtlTypeInfo *deserializeType(MemoryBuffer &type)
  787. {
  788. FieldTypeInfoStruct info;
  789. type.read(info.fieldType);
  790. type.readPacked(info.length);
  791. if (info.fieldType & RFTMhasLocale)
  792. {
  793. const char *locale;
  794. type.read(locale);
  795. info.locale = keep(locale);
  796. }
  797. if (info.fieldType & RFTMhasChildType)
  798. {
  799. unsigned childIdx;
  800. type.readPacked(childIdx);
  801. info.childType = lookupType(childIdx);
  802. }
  803. if (info.fieldType & RFTMhasFields)
  804. {
  805. unsigned numFields;
  806. type.readPacked(numFields);
  807. info.fieldsArray = new const RtlFieldInfo * [numFields+1];
  808. info.fieldsArray[numFields] = nullptr;
  809. for (int n = 0; n < numFields; n++)
  810. {
  811. const char *fieldName;
  812. type.read(fieldName);
  813. unsigned fieldType;
  814. type.readPacked(fieldType);
  815. unsigned fieldFlags;
  816. type.read(fieldFlags);
  817. const char *xpath = nullptr;
  818. if (fieldFlags & RFTMhasXpath)
  819. type.read(xpath);
  820. void *init = nullptr;
  821. if (fieldFlags & RFTMhasInitializer)
  822. {
  823. unsigned initLength;
  824. type.readPacked(initLength);
  825. init = malloc(initLength);
  826. memcpy(init, type.readDirect(initLength), initLength);
  827. }
  828. fieldFlags &= ~RFTMserializerFlags;
  829. info.fieldsArray[n] = new RtlFieldStrInfo(keep(fieldName), keep(xpath), lookupType(fieldType), fieldFlags, (const char *) init);
  830. }
  831. }
  832. info.fieldType &= ~RFTMserializerFlags;
  833. return info.createRtlTypeInfo(callback);
  834. }
  835. void patchIndexFilePos()
  836. {
  837. if (callback && (base->fieldType & RFTMkind) == type_record)
  838. {
  839. // Yukky hack time
  840. // Assumes that the fieldinfo is not shared...
  841. // But that is also assumed by the code that cleans them up.
  842. const RtlFieldInfo * const *fields = base->queryFields();
  843. for(;;)
  844. {
  845. const RtlFieldInfo *field = *fields++;
  846. if (!field)
  847. break;
  848. if (field->type->getType() == type_filepos) // probably blobs too?
  849. {
  850. static_cast<RtlFileposTypeInfo *>(const_cast<RtlTypeInfo *>(field->type))->setCallback(callback);
  851. }
  852. }
  853. }
  854. }
  855. };
  856. extern ECLRTL_API IRtlFieldTypeDeserializer *createRtlFieldTypeDeserializer(IThorIndexCallback *callback)
  857. {
  858. return new CRtlFieldTypeDeserializer(callback);
  859. }
  860. extern ECLRTL_API StringBuffer &dumpTypeInfo(StringBuffer &ret, const RtlTypeInfo *t)
  861. {
  862. return CRtlFieldTypeSerializer::serialize(ret, t);
  863. }
  864. extern ECLRTL_API MemoryBuffer &dumpTypeInfo(MemoryBuffer &ret, const RtlTypeInfo *t, bool useBias)
  865. {
  866. return CRtlFieldTypeBinSerializer::serialize(ret, t, useBias);
  867. }
  868. extern ECLRTL_API void serializeRecordType(size32_t & __lenResult, void * & __result, IOutputMetaData & metaVal)
  869. {
  870. MemoryBuffer ret;
  871. CRtlFieldTypeBinSerializer::serialize(ret, metaVal.queryTypeInfo(), false);
  872. __lenResult = ret.length();
  873. __result = ret.detach();
  874. }
  875. extern ECLRTL_API void dumpRecordType(size32_t & __lenResult,char * & __result,IOutputMetaData &metaVal)
  876. {
  877. StringBuffer ret;
  878. CRtlFieldTypeSerializer::serialize(ret, metaVal.queryTypeInfo());
  879. #ifdef _DEBUG
  880. StringBuffer ret2;
  881. CRtlFieldTypeDeserializer deserializer(nullptr);
  882. CRtlFieldTypeSerializer::serialize(ret2, deserializer.deserialize(ret));
  883. assert(streq(ret, ret2));
  884. MemoryBuffer out;
  885. CRtlFieldTypeBinSerializer::serialize(out, metaVal.queryTypeInfo(), false);
  886. CRtlFieldTypeDeserializer bindeserializer(nullptr);
  887. CRtlFieldTypeSerializer::serialize(ret2.clear(), bindeserializer.deserialize(out));
  888. assert(streq(ret, ret2));
  889. #endif
  890. __lenResult = ret.length();
  891. __result = ret.detach();
  892. }
  893. extern ECLRTL_API void getFieldVal(size32_t & __lenResult,char * & __result, int column, IOutputMetaData & metaVal, const byte *row)
  894. {
  895. __lenResult = 0;
  896. __result = nullptr;
  897. if (column >= 0)
  898. {
  899. const RtlRecord &r = metaVal.queryRecordAccessor(true);
  900. unsigned numOffsets = r.getNumVarFields() + 1;
  901. size_t * variableOffsets = (size_t *)alloca(numOffsets * sizeof(size_t));
  902. RtlRow offsetCalculator(r, row, numOffsets, variableOffsets);
  903. offsetCalculator.getUtf8(__lenResult, __result, column);
  904. }
  905. }
  906. extern ECLRTL_API int getFieldNum(const char *fieldName, IOutputMetaData & metaVal)
  907. {
  908. const RtlRecord r = metaVal.queryRecordAccessor(true);
  909. return r.getFieldNum(fieldName);
  910. }
  911. enum FieldMatchType {
  912. // On a field, exactly one of the below is set, but translator returns a bitmap indicating
  913. // which were required (and we can restrict translation to allow some types but not others)
  914. match_perfect = 0x00, // exact type match - use memcpy
  915. match_link = 0x01, // copy a nested dataset by linking
  916. match_move = 0x02, // at least one field has moved (set on translator)
  917. match_remove = 0x04, // at least one field has been removed (set on translator)
  918. match_truncate = 0x08, // dest is truncated copy of source - use memcpy
  919. match_extend = 0x10, // dest is padded version of source - use memcpy and memset
  920. match_typecast = 0x20, // type has changed - cast required
  921. match_none = 0x40, // No matching field in source - use null value
  922. match_recurse = 0x80, // Use recursive translator for child records/datasets
  923. match_fail = 0x100, // no translation possible
  924. // This flag may be set in conjunction with the others
  925. match_inifblock = 0x200, // matching to a field in an ifblock - may not be present
  926. };
  927. StringBuffer &describeFlags(StringBuffer &out, FieldMatchType flags)
  928. {
  929. if (flags == match_perfect)
  930. return out.append("perfect");
  931. unsigned origlen = out.length();
  932. if (flags & match_link) out.append("|link");
  933. if (flags & match_move) out.append("|move");
  934. if (flags & match_remove) out.append("|remove");
  935. if (flags & match_truncate) out.append("|truncate");
  936. if (flags & match_extend) out.append("|extend");
  937. if (flags & match_typecast) out.append("|typecast");
  938. if (flags & match_none) out.append("|none");
  939. if (flags & match_recurse) out.append("|recurse");
  940. if (flags & match_inifblock) out.append("|ifblock");
  941. if (flags & match_fail) out.append("|fail");
  942. assertex(out.length() > origlen);
  943. return out.remove(origlen, 1);
  944. }
  945. inline constexpr FieldMatchType operator|(FieldMatchType a, FieldMatchType b) { return (FieldMatchType)((int)a | (int)b); }
  946. inline FieldMatchType &operator|=(FieldMatchType &a, FieldMatchType b) { return (FieldMatchType &) ((int &)a |= (int)b); }
  947. class GeneralRecordTranslator : public CInterfaceOf<IDynamicTransform>
  948. {
  949. public:
  950. GeneralRecordTranslator(const RtlRecord &_destRecInfo, const RtlRecord &_srcRecInfo)
  951. : destRecInfo(_destRecInfo), sourceRecInfo(_srcRecInfo)
  952. {
  953. matchInfo = new MatchInfo[destRecInfo.getNumFields()];
  954. createMatchInfo();
  955. }
  956. ~GeneralRecordTranslator()
  957. {
  958. delete [] matchInfo;
  959. }
  960. virtual void describe() const override
  961. {
  962. doDescribe(0);
  963. }
  964. virtual size32_t translate(ARowBuilder &builder, const byte *sourceRec) const override
  965. {
  966. dbgassertex(canTranslate());
  967. return doTranslate(builder, 0, sourceRec);
  968. }
  969. virtual bool canTranslate() const override
  970. {
  971. return (matchFlags & match_fail) == 0;
  972. }
  973. virtual bool needsTranslate() const override
  974. {
  975. return (matchFlags & ~match_link) != 0;
  976. }
  977. private:
  978. void doDescribe(unsigned indent) const
  979. {
  980. for (unsigned idx = 0; idx < destRecInfo.getNumFields(); idx++)
  981. {
  982. const char *source = destRecInfo.queryName(idx);
  983. const MatchInfo &match = matchInfo[idx];
  984. if (match.matchType == match_none)
  985. DBGLOG("%*sNo match for field %s - default value will be used", indent, "", source);
  986. else
  987. {
  988. StringBuffer matchStr;
  989. DBGLOG("%*sMatch (%s) to field %d for field %s (%x)", indent, "", describeFlags(matchStr, match.matchType).str(), match.matchIdx, source, destRecInfo.queryType(idx)->fieldType);
  990. if (match.subTrans)
  991. match.subTrans->doDescribe(indent+2);
  992. }
  993. }
  994. if (!canTranslate())
  995. DBGLOG("%*sTranslation is NOT possible", indent, "");
  996. else if (needsTranslate())
  997. {
  998. StringBuffer matchStr;
  999. DBGLOG("%*sTranslation is possible (%s)", indent, "", describeFlags(matchStr, matchFlags).str());
  1000. }
  1001. else
  1002. DBGLOG("%*sTranslation is not necessary", indent, "");
  1003. }
  1004. size32_t doTranslate(ARowBuilder &builder, size32_t offset, const byte *sourceRec) const
  1005. {
  1006. unsigned numOffsets = sourceRecInfo.getNumVarFields() + 1;
  1007. size_t * variableOffsets = (size_t *)alloca(numOffsets * sizeof(size_t));
  1008. byte * destConditions = (byte *)alloca(destRecInfo.getNumIfBlocks() * sizeof(byte));
  1009. RtlRow sourceRow(sourceRecInfo, sourceRec, numOffsets, variableOffsets);
  1010. size32_t estimate = destRecInfo.getFixedSize();
  1011. if (!estimate)
  1012. {
  1013. estimate = estimateNewSize(sourceRow);
  1014. builder.ensureCapacity(offset+estimate, "record");
  1015. }
  1016. size32_t origOffset = offset;
  1017. for (unsigned idx = 0; idx < destRecInfo.getNumFields(); idx++)
  1018. {
  1019. const RtlFieldInfo *field = destRecInfo.queryField(idx);
  1020. if (field->omitable() && destRecInfo.excluded(field, builder.getSelf(), destConditions))
  1021. continue;
  1022. const RtlTypeInfo *type = field->type;
  1023. const MatchInfo &match = matchInfo[idx];
  1024. if (match.matchType == match_none || match.matchType==match_fail)
  1025. offset = type->buildNull(builder, offset, field);
  1026. else
  1027. {
  1028. unsigned matchField = match.matchIdx;
  1029. const RtlTypeInfo *sourceType = sourceRecInfo.queryType(matchField);
  1030. size_t sourceOffset = sourceRow.getOffset(matchField);
  1031. const byte *source = sourceRec + sourceOffset;
  1032. size_t copySize = sourceRow.getSize(matchField);
  1033. if (copySize == 0 && (match.matchType & match_inifblock)) // Field is missing because of an ifblock - use default value
  1034. {
  1035. offset = type->buildNull(builder, offset, field);
  1036. }
  1037. else
  1038. {
  1039. switch (match.matchType & ~match_inifblock)
  1040. {
  1041. case match_perfect:
  1042. {
  1043. // Look ahead for other perfect matches and combine the copies
  1044. while (idx < destRecInfo.getNumFields()-1)
  1045. {
  1046. const MatchInfo &nextMatch = matchInfo[idx+1];
  1047. if (nextMatch.matchType == match_perfect && nextMatch.matchIdx == matchField+1)
  1048. {
  1049. idx++;
  1050. matchField++;
  1051. }
  1052. else
  1053. break;
  1054. }
  1055. size_t copySize = sourceRow.getOffset(matchField+1) - sourceOffset;
  1056. builder.ensureCapacity(offset+copySize, field->name);
  1057. memcpy(builder.getSelf()+offset, source, copySize);
  1058. offset += copySize;
  1059. break;
  1060. }
  1061. case match_truncate:
  1062. {
  1063. assert(type->isFixedSize());
  1064. size32_t copySize = type->getMinSize();
  1065. builder.ensureCapacity(offset+copySize, field->name);
  1066. memcpy(builder.getSelf()+offset, source, copySize);
  1067. offset += copySize;
  1068. break;
  1069. }
  1070. case match_extend:
  1071. {
  1072. assert(type->isFixedSize());
  1073. size32_t destSize = type->getMinSize();
  1074. builder.ensureCapacity(offset+destSize, field->name);
  1075. memcpy(builder.getSelf()+offset, source, copySize);
  1076. offset += copySize;
  1077. unsigned fillSize = destSize - copySize;
  1078. memset(builder.getSelf()+offset, match.fillChar, fillSize);
  1079. offset += fillSize;
  1080. break;
  1081. }
  1082. case match_typecast:
  1083. offset = translateScalar(builder, offset, field, type, sourceType, source);
  1084. break;
  1085. case match_link:
  1086. {
  1087. // a 32-bit record count, and a (linked) pointer to an array of record pointers
  1088. byte *dest = builder.ensureCapacity(offset+sizeof(size32_t)+sizeof(const byte **), field->name)+offset;
  1089. *(size32_t *)dest = *(size32_t *)source;
  1090. *(const byte ***)(dest + sizeof(size32_t)) = rtlLinkRowset(*(const byte ***)(source + sizeof(size32_t)));
  1091. offset += sizeof(size32_t)+sizeof(const byte **);
  1092. break;
  1093. }
  1094. case match_recurse:
  1095. if (type->getType()==type_record)
  1096. offset = match.subTrans->doTranslate(builder, offset, source);
  1097. else if (type->isLinkCounted())
  1098. {
  1099. // a 32-bit record count, and a pointer to an array of record pointers
  1100. IEngineRowAllocator *childAllocator = builder.queryAllocator()->createChildRowAllocator(type->queryChildType());
  1101. assertex(childAllocator); // May not be available when using serialized types (but unlikely to want to create linkcounted children remotely either)
  1102. size32_t sizeInBytes = sizeof(size32_t) + sizeof(void *);
  1103. builder.ensureCapacity(offset+sizeInBytes, field->name);
  1104. size32_t numRows = 0;
  1105. const byte **childRows = nullptr;
  1106. if (sourceType->isLinkCounted())
  1107. {
  1108. // a 32-bit count, then a pointer to the source rows
  1109. size32_t childCount = *(size32_t *) source;
  1110. source += sizeof(size32_t);
  1111. const byte ** sourceRows = *(const byte***) source;
  1112. for (size32_t childRow = 0; childRow < childCount; childRow++)
  1113. {
  1114. RtlDynamicRowBuilder childBuilder(*childAllocator);
  1115. size32_t childLen = match.subTrans->doTranslate(childBuilder, 0, sourceRows[childRow]);
  1116. childRows = childAllocator->appendRowOwn(childRows, ++numRows, (void *) childBuilder.finalizeRowClear(childLen));
  1117. }
  1118. }
  1119. else
  1120. {
  1121. // a 32-bit size, then rows inline
  1122. size32_t childSize = *(size32_t *) source;
  1123. source += sizeof(size32_t);
  1124. const byte *initialSource = source;
  1125. while ((size_t)(source - initialSource) < childSize)
  1126. {
  1127. RtlDynamicRowBuilder childBuilder(*childAllocator);
  1128. size32_t childLen = match.subTrans->doTranslate(childBuilder, 0, source);
  1129. childRows = childAllocator->appendRowOwn(childRows, ++numRows, (void *) childBuilder.finalizeRowClear(childLen));
  1130. source += sourceType->queryChildType()->size(source, nullptr); // MORE - shame to repeat a calculation that the translate above almost certainly just did
  1131. }
  1132. }
  1133. // Go back in and patch the count, remembering it may have moved
  1134. rtlWriteInt4(builder.getSelf()+offset, numRows);
  1135. * ( const void * * ) (builder.getSelf()+offset+sizeof(size32_t)) = childRows;
  1136. offset += sizeInBytes;
  1137. }
  1138. else
  1139. {
  1140. size32_t countOffset = offset;
  1141. byte *dest = builder.ensureCapacity(offset+sizeof(size32_t), field->name)+offset;
  1142. offset += sizeof(size32_t);
  1143. size32_t initialOffset = offset;
  1144. *(size32_t *)dest = 0; // patched below when true figure known
  1145. if (sourceType->isLinkCounted())
  1146. {
  1147. // a 32-bit count, then a pointer to the source rows
  1148. size32_t childCount = *(size32_t *) source;
  1149. source += sizeof(size32_t);
  1150. const byte ** sourceRows = *(const byte***) source;
  1151. for (size32_t childRow = 0; childRow < childCount; childRow++)
  1152. {
  1153. offset = match.subTrans->doTranslate(builder, offset, sourceRows[childRow]);
  1154. }
  1155. }
  1156. else
  1157. {
  1158. // a 32-bit size, then rows inline
  1159. size32_t childSize = *(size32_t *) source;
  1160. source += sizeof(size32_t);
  1161. const byte *initialSource = source;
  1162. while ((size_t)(source - initialSource) < childSize)
  1163. {
  1164. offset = match.subTrans->doTranslate(builder, offset, source);
  1165. source += sourceType->queryChildType()->size(source, nullptr); // MORE - shame to repeat a calculation that the translate above almost certainly just did
  1166. }
  1167. }
  1168. dest = builder.getSelf() + countOffset; // Note - may have been moved by reallocs since last calculated
  1169. *(size32_t *)dest = offset - initialOffset;
  1170. }
  1171. break;
  1172. default:
  1173. throwUnexpected();
  1174. }
  1175. }
  1176. }
  1177. }
  1178. if (estimate && offset-origOffset != estimate)
  1179. {
  1180. // Note - ifblocks make this assertion invalid. We do not account for potentially omitted fields
  1181. // when estimating target record size.
  1182. if (!destRecInfo.getNumIfBlocks())
  1183. assert(offset-origOffset > estimate); // Estimate is always supposed to be conservative
  1184. #ifdef TRACE_TRANSLATION
  1185. DBGLOG("Wrote %u bytes to record (estimate was %u)\n", offset-origOffset, estimate);
  1186. #endif
  1187. }
  1188. return offset;
  1189. }
  1190. inline FieldMatchType match() const
  1191. {
  1192. return matchFlags;
  1193. }
  1194. const RtlRecord &destRecInfo;
  1195. const RtlRecord &sourceRecInfo;
  1196. unsigned fixedDelta = 0; // total size of all fixed-size source fields that are not matched
  1197. UnsignedArray unmatched; // List of all variable-size source fields that are unmatched
  1198. FieldMatchType matchFlags = match_perfect;
  1199. struct MatchInfo
  1200. {
  1201. unsigned matchIdx = 0;
  1202. FieldMatchType matchType = match_fail;
  1203. char fillChar = 0;
  1204. GeneralRecordTranslator *subTrans = nullptr;
  1205. ~MatchInfo()
  1206. {
  1207. delete subTrans;
  1208. }
  1209. } *matchInfo;
  1210. static size32_t translateScalar(ARowBuilder &builder, size32_t offset, const RtlFieldInfo *field, const RtlTypeInfo *destType, const RtlTypeInfo *sourceType, const byte *source)
  1211. {
  1212. // This code COULD move into rtlfield.cpp?
  1213. switch(destType->getType())
  1214. {
  1215. case type_filepos:
  1216. case type_boolean:
  1217. case type_int:
  1218. case type_swapint:
  1219. case type_packedint:
  1220. offset = destType->buildInt(builder, offset, field, sourceType->getInt(source));
  1221. break;
  1222. case type_real:
  1223. offset = destType->buildReal(builder, offset, field, sourceType->getReal(source));
  1224. break;
  1225. case type_decimal: // Go via string - not common enough to special-case
  1226. case type_data:
  1227. case type_string:
  1228. case type_varstring:
  1229. case type_qstring:
  1230. {
  1231. size32_t size;
  1232. rtlDataAttr text;
  1233. sourceType->getString(size, text.refstr(), source);
  1234. offset = destType->buildString(builder, offset, field, size, text.getstr());
  1235. break;
  1236. }
  1237. case type_unicode:
  1238. case type_varunicode:
  1239. case type_utf8:
  1240. {
  1241. size32_t utf8chars;
  1242. rtlDataAttr utf8Text;
  1243. sourceType->getUtf8(utf8chars, utf8Text.refstr(), source);
  1244. offset = destType->buildUtf8(builder, offset, field, utf8chars, utf8Text.getstr());
  1245. break;
  1246. }
  1247. case type_set:
  1248. {
  1249. bool isAll = *(bool *) source;
  1250. source+= sizeof(bool);
  1251. byte *dest = builder.ensureCapacity(offset+sizeof(bool)+sizeof(size32_t), field->name)+offset;
  1252. *(size32_t *) (dest + sizeof(bool)) = 0; // Patch later when size known
  1253. offset += sizeof(bool) + sizeof(size32_t);
  1254. if (isAll)
  1255. {
  1256. *(bool*) dest = true;
  1257. }
  1258. else
  1259. {
  1260. *(bool*) dest = false;
  1261. size32_t sizeOffset = offset - sizeof(size32_t); // Where we need to patch
  1262. size32_t childSize = *(size32_t *)source;
  1263. source += sizeof(size32_t);
  1264. const byte *initialSource = source;
  1265. size32_t initialOffset = offset;
  1266. const RtlTypeInfo *destChildType = destType->queryChildType();
  1267. const RtlTypeInfo *sourceChildType = sourceType->queryChildType();
  1268. while ((size_t)(source - initialSource) < childSize)
  1269. {
  1270. offset = translateScalar(builder, offset, field, destChildType, sourceChildType, source);
  1271. source += sourceChildType->size(source, nullptr); // MORE - shame to repeat a calculation that the translate above almost certainly just did
  1272. }
  1273. dest = builder.getSelf() + sizeOffset; // Note - man have been moved by reallocs since last calculated
  1274. *(size32_t *)dest = offset - initialOffset;
  1275. }
  1276. break;
  1277. }
  1278. default:
  1279. throwUnexpected();
  1280. }
  1281. return offset;
  1282. }
  1283. size32_t estimateNewSize(const RtlRow &sourceRow) const
  1284. {
  1285. //DBGLOG("Source record size is %d", (int) sourceRow.getRecordSize());
  1286. size32_t expectedSize = sourceRow.getRecordSize() - fixedDelta;
  1287. //DBGLOG("Source record size without omitted fixed size fields is %d", expectedSize);
  1288. ForEachItemIn(i, unmatched)
  1289. {
  1290. unsigned fieldNo = unmatched.item(i);
  1291. expectedSize -= sourceRow.getSize(fieldNo);
  1292. //DBGLOG("Reducing estimated size by %d to %d for omitted field %d (%s)", (int) sourceRow.getSize(fieldNo), expectedSize, fieldNo, sourceRecInfo.queryName(fieldNo));
  1293. }
  1294. if (matchFlags & ~(match_perfect|match_link|match_none|match_extend|match_truncate))
  1295. {
  1296. for (unsigned idx = 0; idx < destRecInfo.getNumFields(); idx++)
  1297. {
  1298. const MatchInfo &match = matchInfo[idx];
  1299. const RtlTypeInfo *type = destRecInfo.queryType(idx);
  1300. unsigned matchField = match.matchIdx;
  1301. switch (match.matchType)
  1302. {
  1303. case match_perfect:
  1304. case match_link:
  1305. case match_none:
  1306. case match_extend:
  1307. case match_truncate:
  1308. // These ones were already included in fixedDelta
  1309. break;
  1310. default:
  1311. // This errs on the side of small - i.e. it assumes that all typecasts end up at minimum size
  1312. // We could do better in some cases e.g. variable string <-> variable unicode we can assume factor of 2,
  1313. // uft8 <-> string we could calculate here - but unlikely to be worth the effort.
  1314. // But it's fine for fixed size output fields, including truncate/extend
  1315. // We could also precalculate the expected delta if all omitted fields are fixed size - but not sure how likely/worthwhile that is.
  1316. expectedSize += type->getMinSize() - sourceRow.getSize(matchField);
  1317. //DBGLOG("Adjusting estimated size by (%d - %d) to %d for translated field %d (%s)", (int) sourceRow.getSize(matchField), type->getMinSize(), expectedSize, matchField, sourceRecInfo.queryName(matchField));
  1318. break;
  1319. }
  1320. }
  1321. }
  1322. return expectedSize;
  1323. }
  1324. void createMatchInfo()
  1325. {
  1326. for (unsigned idx = 0; idx < destRecInfo.getNumFields(); idx++)
  1327. {
  1328. const RtlFieldInfo *field = destRecInfo.queryField(idx);
  1329. const RtlTypeInfo *type = field->type;
  1330. MatchInfo &info = matchInfo[idx];
  1331. info.matchIdx = sourceRecInfo.getFieldNum(destRecInfo.queryName(idx));
  1332. if (info.matchIdx == -1)
  1333. {
  1334. info.matchType = match_none;
  1335. size32_t defaultSize = field->initializer ? type->size(field->initializer, nullptr) : type->getMinSize();
  1336. fixedDelta -= defaultSize;
  1337. //DBGLOG("Decreasing fixedDelta size by %d to %d for defaulted field %d (%s)", defaultSize, fixedDelta, idx, destRecInfo.queryName(idx));
  1338. }
  1339. else
  1340. {
  1341. const RtlTypeInfo *sourceType = sourceRecInfo.queryType(info.matchIdx);
  1342. if (!type->isScalar() || !sourceType->isScalar())
  1343. {
  1344. if (type->getType() != sourceType->getType())
  1345. info.matchType = match_fail; // No translation from one non-scalar type to another
  1346. else
  1347. {
  1348. switch (type->getType())
  1349. {
  1350. case type_set:
  1351. if (type->queryChildType()->fieldType==sourceType->queryChildType()->fieldType &&
  1352. type->queryChildType()->length==sourceType->queryChildType()->length)
  1353. info.matchType = match_perfect;
  1354. else
  1355. info.matchType = match_typecast;
  1356. break;
  1357. case type_row: // These are not expected I think...
  1358. throwUnexpected();
  1359. case type_ifblock:
  1360. case type_record:
  1361. case type_table:
  1362. {
  1363. const RtlRecord *subDest = destRecInfo.queryNested(idx);
  1364. const RtlRecord *subSrc = sourceRecInfo.queryNested(info.matchIdx);
  1365. info.subTrans = new GeneralRecordTranslator(*subDest, *subSrc);
  1366. if (!info.subTrans->needsTranslate())
  1367. {
  1368. // Child does not require translation, but check linkcount mode matches too!
  1369. if (type->isLinkCounted())
  1370. if (sourceType->isLinkCounted())
  1371. info.matchType = match_link;
  1372. else
  1373. info.matchType = match_recurse;
  1374. else
  1375. if (sourceType->isLinkCounted())
  1376. info.matchType = match_recurse;
  1377. else
  1378. info.matchType = match_perfect;
  1379. if (info.matchType != match_recurse)
  1380. {
  1381. delete info.subTrans;
  1382. info.subTrans = nullptr;
  1383. }
  1384. }
  1385. else if (info.subTrans->canTranslate())
  1386. {
  1387. info.matchType = match_recurse;
  1388. matchFlags |= info.subTrans->matchFlags;
  1389. }
  1390. else
  1391. info.matchType = match_fail;
  1392. break;
  1393. }
  1394. default:
  1395. info.matchType = match_fail;
  1396. break;
  1397. }
  1398. }
  1399. }
  1400. else if (type->fieldType==sourceType->fieldType && type->fieldType != type_filepos)
  1401. {
  1402. if (type->length==sourceType->length)
  1403. {
  1404. info.matchType = match_perfect;
  1405. }
  1406. else
  1407. {
  1408. assert(type->isFixedSize()); // Both variable size would have matched length above
  1409. info.matchType = match_typecast;
  1410. if (type->length < sourceType->length)
  1411. {
  1412. if (type->canTruncate())
  1413. {
  1414. info.matchType = match_truncate;
  1415. fixedDelta += sourceType->getMinSize()-type->getMinSize();
  1416. //DBGLOG("Increasing fixedDelta size by %d to %d for truncated field %d (%s)", sourceType->getMinSize()-type->getMinSize(), fixedDelta, idx, destRecInfo.queryName(idx));
  1417. }
  1418. }
  1419. else
  1420. {
  1421. if (type->canExtend(info.fillChar))
  1422. {
  1423. info.matchType = match_extend;
  1424. fixedDelta += sourceType->getMinSize()-type->getMinSize();
  1425. //DBGLOG("Decreasing fixedDelta size by %d to %d for truncated field %d (%s)", type->getMinSize()-sourceType->getMinSize(), fixedDelta, idx, destRecInfo.queryName(idx));
  1426. }
  1427. }
  1428. }
  1429. }
  1430. else
  1431. info.matchType = match_typecast;
  1432. if (sourceRecInfo.queryField(info.matchIdx)->flags & RFTMinifblock)
  1433. info.matchType |= match_inifblock; // Avoids incorrect commoning up of adjacent matches
  1434. // MORE - could note the highest interesting fieldnumber in the source and not bother filling in offsets after that
  1435. // Not sure it would help much though - usually need to know the total record size anyway in real life
  1436. if (idx != info.matchIdx)
  1437. matchFlags |= match_move;
  1438. }
  1439. matchFlags |= info.matchType;
  1440. }
  1441. if (sourceRecInfo.getNumFields() > destRecInfo.getNumFields())
  1442. matchFlags |= match_remove;
  1443. if (matchFlags && !destRecInfo.getFixedSize())
  1444. {
  1445. for (unsigned idx = 0; idx < sourceRecInfo.getNumFields(); idx++)
  1446. {
  1447. const RtlFieldInfo *field = sourceRecInfo.queryField(idx);
  1448. const RtlTypeInfo *type = field->type;
  1449. if (destRecInfo.getFieldNum(field->name) == (unsigned) -1)
  1450. {
  1451. // unmatched field
  1452. if (type->isFixedSize())
  1453. {
  1454. //DBGLOG("Reducing estimated size by %d for (fixed size) omitted field %s", (int) type->getMinSize(), field->name);
  1455. fixedDelta += type->getMinSize();
  1456. }
  1457. else
  1458. unmatched.append(idx);
  1459. }
  1460. }
  1461. //DBGLOG("Source record contains %d bytes of omitted fixed size fields", fixedDelta);
  1462. }
  1463. }
  1464. };
  1465. extern ECLRTL_API const IDynamicTransform *createRecordTranslator(const RtlRecord &_destRecInfo, const RtlRecord &_srcRecInfo)
  1466. {
  1467. return new GeneralRecordTranslator(_destRecInfo, _srcRecInfo);
  1468. }
  1469. class TranslatedRowStream : public CInterfaceOf<IRowStream>
  1470. {
  1471. public:
  1472. TranslatedRowStream(IRowStream *_inputStream, IEngineRowAllocator *_resultAllocator, const RtlRecord &outputRecord, const RtlRecord &inputRecord)
  1473. : inputStream(_inputStream), resultAllocator(_resultAllocator), translator(new GeneralRecordTranslator(outputRecord, inputRecord))
  1474. {
  1475. translator->describe();
  1476. }
  1477. virtual const void *nextRow()
  1478. {
  1479. if (eof)
  1480. return NULL;
  1481. const void *inRow = inputStream->nextRow();
  1482. if (!inRow)
  1483. {
  1484. if (eogSeen)
  1485. eof = true;
  1486. else
  1487. eogSeen = true;
  1488. return nullptr;
  1489. }
  1490. else
  1491. eogSeen = false;
  1492. RtlDynamicRowBuilder rowBuilder(resultAllocator);
  1493. size32_t len = translator->translate(rowBuilder, (const byte *) inRow);
  1494. rtlReleaseRow(inRow);
  1495. return rowBuilder.finalizeRowClear(len);
  1496. }
  1497. virtual void stop() override
  1498. {
  1499. resultAllocator.clear();
  1500. }
  1501. bool canTranslate() const
  1502. {
  1503. return translator->canTranslate();
  1504. }
  1505. bool needsTranslate() const
  1506. {
  1507. return translator->needsTranslate();
  1508. }
  1509. protected:
  1510. Linked<IRowStream> inputStream;
  1511. Linked<IEngineRowAllocator> resultAllocator;
  1512. Owned<const IDynamicTransform> translator;
  1513. unsigned numOffsets = 0;
  1514. size_t * variableOffsets = nullptr;
  1515. bool eof = false;
  1516. bool eogSeen = false;
  1517. };
  1518. extern ECLRTL_API IRowStream * transformRecord(IEngineRowAllocator * resultAllocator,IOutputMetaData & metaInput,IRowStream * input)
  1519. {
  1520. if (resultAllocator->queryOutputMeta()==&metaInput)
  1521. return LINK(input);
  1522. Owned<TranslatedRowStream> stream = new TranslatedRowStream(input, resultAllocator,
  1523. resultAllocator->queryOutputMeta()->queryRecordAccessor(true),
  1524. metaInput.queryRecordAccessor(true));
  1525. if (!stream->needsTranslate())
  1526. return LINK(input);
  1527. else if (!stream->canTranslate())
  1528. rtlFail(0, "Cannot translate record stream");
  1529. else
  1530. return stream.getClear();
  1531. }