ObjectStreamIn.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <Jolt/Jolt.h>
  4. #include <Jolt/Core/Factory.h>
  5. #include <Jolt/ObjectStream/ObjectStreamIn.h>
  6. #include <Jolt/ObjectStream/ObjectStreamTextIn.h>
  7. #include <Jolt/ObjectStream/ObjectStreamBinaryIn.h>
  8. #include <Jolt/ObjectStream/SerializableObject.h>
  9. JPH_SUPPRESS_WARNINGS_STD_BEGIN
  10. #include <unordered_set>
  11. JPH_SUPPRESS_WARNINGS_STD_END
  12. JPH_NAMESPACE_BEGIN
  13. ObjectStreamIn::ObjectStreamIn(istream &inStream) :
  14. mStream(inStream)
  15. {
  16. }
  17. bool ObjectStreamIn::GetInfo(istream &inStream, EStreamType &outType, int &outVersion, int &outRevision)
  18. {
  19. // Read header and check if it is the correct format, e.g. "TOS 1.00"
  20. char header[9];
  21. memset(header, 0, 9);
  22. inStream.read(header, 8);
  23. if ((header[0] == 'B' || header[0] == 'T') && header[1] == 'O' && header[2] == 'S'
  24. && (header[3] == ' ' || isdigit(header[3])) && isdigit(header[4])
  25. && header[5] == '.' && isdigit(header[6]) && isdigit(header[7]))
  26. {
  27. // Check if this is a binary or text objectfile
  28. switch (header[0])
  29. {
  30. case 'T': outType = ObjectStream::EStreamType::Text; break;
  31. case 'B': outType = ObjectStream::EStreamType::Binary; break;
  32. default: JPH_ASSERT(false); break;
  33. }
  34. // Extract version and revision
  35. header[5] = '\0';
  36. outVersion = atoi(&header[3]);
  37. outRevision = atoi(&header[6]);
  38. return true;
  39. }
  40. Trace("ObjectStreamIn: Not a valid object stream.");
  41. return false;
  42. }
  43. ObjectStreamIn *ObjectStreamIn::Open(istream &inStream)
  44. {
  45. // Check if file is an ObjectStream of the correct version and revision
  46. EStreamType type;
  47. int version;
  48. int revision;
  49. if (GetInfo(inStream, type, version, revision))
  50. {
  51. if (version == sVersion && revision == sRevision)
  52. {
  53. // Create an input stream of the correct type
  54. switch (type)
  55. {
  56. case EStreamType::Text: return new ObjectStreamTextIn(inStream);
  57. case EStreamType::Binary: return new ObjectStreamBinaryIn(inStream);
  58. default: JPH_ASSERT(false);
  59. }
  60. }
  61. else
  62. {
  63. Trace("ObjectStreamIn: Different version stream (%d.%02d, expected %d.%02d).", version, revision, sVersion, sRevision);
  64. }
  65. }
  66. return nullptr;
  67. }
  68. void *ObjectStreamIn::Read(const RTTI *inRTTI)
  69. {
  70. using ObjectSet = unordered_set<void *>;
  71. // Read all information on the stream
  72. void *main_object = nullptr;
  73. bool continue_reading = true;
  74. for (;;)
  75. {
  76. // Get type of next operation
  77. EOSDataType data_type;
  78. if (!ReadDataType(data_type))
  79. break;
  80. if (data_type == EOSDataType::Declare)
  81. {
  82. // Read type declaration
  83. if (!ReadRTTI())
  84. {
  85. Trace("ObjectStreamIn: Fatal error while reading class description for class %s.", inRTTI->GetName());
  86. continue_reading = false;
  87. break;
  88. }
  89. }
  90. else if (data_type == EOSDataType::Object)
  91. {
  92. const RTTI *rtti;
  93. void *object = ReadObject(rtti);
  94. if (!main_object && object)
  95. {
  96. // This is the first and thus main object of the file.
  97. if (rtti->IsKindOf(inRTTI))
  98. {
  99. // Object is of correct type
  100. main_object = object;
  101. }
  102. else
  103. {
  104. Trace("ObjectStreamIn: Main object of different type. Expected %s, but found %s.", inRTTI->GetName(), rtti->GetName());
  105. continue_reading = false;
  106. break;
  107. }
  108. }
  109. }
  110. else
  111. {
  112. // Invalid or out of place token found
  113. Trace("ObjectStreamIn: Invalid or out of place token found.");
  114. continue_reading = false;
  115. break;
  116. }
  117. }
  118. // Resolve links (pointer, references)
  119. if (continue_reading)
  120. {
  121. // Resolve links
  122. ObjectSet referenced_objects;
  123. for (Link &link : mUnresolvedLinks)
  124. {
  125. IdentifierMap::const_iterator j = mIdentifierMap.find(link.mIdentifier);
  126. if (j != mIdentifierMap.end() && j->second.mRTTI->IsKindOf(link.mRTTI))
  127. {
  128. const ObjectInfo &obj_info = j->second;
  129. // Set pointer
  130. *link.mPointer = obj_info.mInstance;
  131. // Increment refcount if it was a referencing pointer
  132. if (link.mRefCountOffset != -1)
  133. ++(*(uint32 *)(((uint8 *)obj_info.mInstance) + link.mRefCountOffset));
  134. // Add referenced object to the list
  135. if (referenced_objects.find(obj_info.mInstance) == referenced_objects.end())
  136. referenced_objects.insert(obj_info.mInstance);
  137. }
  138. else
  139. {
  140. // Referenced object not found, set pointer to nullptr
  141. Trace("ObjectStreamIn: Setting incorrect pointer to class of type %s to nullptr.", link.mRTTI->GetName());
  142. *link.mPointer = nullptr;
  143. }
  144. }
  145. // Release unreferenced objects except the main object
  146. for (const IdentifierMap::value_type &j : mIdentifierMap)
  147. {
  148. const ObjectInfo &obj_info = j.second;
  149. if (obj_info.mInstance != main_object)
  150. {
  151. ObjectSet::const_iterator k = referenced_objects.find(obj_info.mInstance);
  152. if (k == referenced_objects.end())
  153. {
  154. Trace("ObjectStreamIn: Releasing unreferenced object of type %s.", obj_info.mRTTI->GetName());
  155. obj_info.mRTTI->DestructObject(obj_info.mInstance);
  156. }
  157. }
  158. }
  159. return main_object;
  160. }
  161. else
  162. {
  163. // Release all objects if a fatal error occurred
  164. for (const IdentifierMap::value_type &i : mIdentifierMap)
  165. {
  166. const ObjectInfo &obj_info = i.second;
  167. obj_info.mRTTI->DestructObject(obj_info.mInstance);
  168. }
  169. return nullptr;
  170. }
  171. }
  172. void *ObjectStreamIn::ReadObject(const RTTI *& outRTTI)
  173. {
  174. // Read the object class
  175. void *object = nullptr;
  176. string class_name;
  177. if (ReadName(class_name))
  178. {
  179. // Get class description
  180. ClassDescriptionMap::iterator i = mClassDescriptionMap.find(class_name);
  181. if (i != mClassDescriptionMap.end())
  182. {
  183. const ClassDescription &class_desc = i->second;
  184. // Read object identifier
  185. Identifier identifier;
  186. if (ReadIdentifier(identifier))
  187. {
  188. // Check if this object can be read or must be skipped
  189. if (identifier != sNullIdentifier
  190. && class_desc.mRTTI
  191. && !class_desc.mRTTI->IsAbstract())
  192. {
  193. // Create object instance
  194. outRTTI = class_desc.mRTTI;
  195. object = outRTTI->CreateObject();
  196. // Read object attributes
  197. if (ReadClassData(class_desc, object))
  198. {
  199. // Add object to identifier map
  200. mIdentifierMap.try_emplace(identifier, object, outRTTI);
  201. }
  202. else
  203. {
  204. // Fatal error while reading attributes, release object
  205. outRTTI->DestructObject(object);
  206. object = nullptr;
  207. }
  208. }
  209. else
  210. {
  211. // Skip this object
  212. // TODO: This operation can fail, but there is no check yet
  213. Trace("ObjectStreamIn: Found uncreatable object %s.", class_name.c_str());
  214. ReadClassData(class_desc, nullptr);
  215. }
  216. }
  217. }
  218. else
  219. {
  220. // TODO: This is a fatal error, but this function has no way of indicating this
  221. Trace("ObjectStreamIn: Found object of unknown class %s.", class_name.c_str());
  222. }
  223. }
  224. return object;
  225. }
  226. bool ObjectStreamIn::ReadRTTI()
  227. {
  228. // Read class name and find it's attribute info
  229. string class_name;
  230. if (!ReadName(class_name))
  231. return false;
  232. // Find class
  233. const RTTI *rtti = Factory::sInstance->Find(class_name.c_str());
  234. if (rtti == nullptr)
  235. Trace("ObjectStreamIn: Unknown class: \"%s\".", class_name.c_str());
  236. // Insert class description
  237. ClassDescription &class_desc = mClassDescriptionMap.try_emplace(class_name, rtti).first->second;
  238. // Read the number of entries in the description
  239. uint32 count;
  240. if (!ReadCount(count))
  241. return false;
  242. // Read the entries
  243. for (uint32 i = 0; i < count; ++i)
  244. {
  245. AttributeDescription attribute;
  246. // Read name
  247. string attribute_name;
  248. if (!ReadName(attribute_name))
  249. return false;
  250. // Read type
  251. if (!ReadDataType(attribute.mDataType))
  252. return false;
  253. // Read array depth
  254. while (attribute.mDataType == EOSDataType::Array)
  255. {
  256. ++attribute.mArrayDepth;
  257. if (!ReadDataType(attribute.mDataType))
  258. return false;
  259. }
  260. // Read instance/pointer class name
  261. if ((attribute.mDataType == EOSDataType::Instance || attribute.mDataType == EOSDataType::Pointer)
  262. && !ReadName(attribute.mClassName))
  263. return false;
  264. // Find attribute in rtti
  265. if (rtti)
  266. {
  267. // Find attribute index
  268. for (int idx = 0; idx < rtti->GetAttributeCount(); ++idx)
  269. {
  270. const SerializableAttribute &attr = rtti->GetAttribute(idx);
  271. if (strcmp(attr.GetName(), attribute_name.c_str()) == 0)
  272. {
  273. attribute.mIndex = idx;
  274. break;
  275. }
  276. }
  277. // Check if attribute is of expected type
  278. if (attribute.mIndex >= 0)
  279. {
  280. const SerializableAttribute &attr = rtti->GetAttribute(attribute.mIndex);
  281. if (!attr.IsType(attribute.mArrayDepth, attribute.mDataType, attribute.mClassName.c_str()))
  282. attribute.mIndex = -1;
  283. }
  284. }
  285. // Add attribute to the class description
  286. class_desc.mAttributes.push_back(attribute);
  287. }
  288. return true;
  289. }
  290. bool ObjectStreamIn::ReadClassData(const char *inClassName, void *inInstance)
  291. {
  292. // Find the class description
  293. ClassDescriptionMap::iterator i = mClassDescriptionMap.find(inClassName);
  294. if (i != mClassDescriptionMap.end())
  295. return ReadClassData(i->second, inInstance);
  296. return false;
  297. }
  298. bool ObjectStreamIn::ReadClassData(const ClassDescription &inClassDesc, void *inInstance)
  299. {
  300. // Read data for this class
  301. bool continue_reading = true;
  302. for (const AttributeDescription &attr_desc : inClassDesc.mAttributes)
  303. {
  304. // Read or skip the attribute data
  305. if (attr_desc.mIndex >= 0 && inInstance)
  306. {
  307. const SerializableAttribute &attr = inClassDesc.mRTTI->GetAttribute(attr_desc.mIndex);
  308. continue_reading = attr.ReadData(*this, inInstance);
  309. }
  310. else
  311. continue_reading = SkipAttributeData(attr_desc.mArrayDepth, attr_desc.mDataType, attr_desc.mClassName.c_str());
  312. if (!continue_reading)
  313. break;
  314. }
  315. return continue_reading;
  316. }
  317. bool ObjectStreamIn::ReadPointerData(const RTTI *inRTTI, void **inPointer, int inRefCountOffset)
  318. {
  319. Identifier identifier;
  320. if (ReadIdentifier(identifier))
  321. {
  322. if (identifier == sNullIdentifier)
  323. {
  324. // Set nullptr pointer
  325. inPointer = nullptr;
  326. }
  327. else
  328. {
  329. // Put pointer on the list to be resolved later on
  330. mUnresolvedLinks.emplace_back();
  331. mUnresolvedLinks.back().mPointer = inPointer;
  332. mUnresolvedLinks.back().mRefCountOffset = inRefCountOffset;
  333. mUnresolvedLinks.back().mIdentifier = identifier;
  334. mUnresolvedLinks.back().mRTTI = inRTTI;
  335. }
  336. return true;
  337. }
  338. return false;
  339. }
  340. bool ObjectStreamIn::SkipAttributeData(int inArrayDepth, EOSDataType inDataType, const char *inClassName)
  341. {
  342. bool continue_reading = true;
  343. // Get number of items to read
  344. uint32 count = 1;
  345. for (; inArrayDepth > 0; --inArrayDepth)
  346. {
  347. uint32 temporary;
  348. if (ReadCount(temporary))
  349. {
  350. // Multiply for multi dimensional arrays
  351. count *= temporary;
  352. }
  353. else
  354. {
  355. // Fatal error while reading array size
  356. continue_reading = false;
  357. break;
  358. }
  359. }
  360. // Read data for all items
  361. if (continue_reading)
  362. {
  363. if (inDataType == EOSDataType::Instance)
  364. {
  365. // Get the class description
  366. ClassDescriptionMap::iterator i = mClassDescriptionMap.find(inClassName);
  367. if (i != mClassDescriptionMap.end())
  368. {
  369. for (; count > 0 && continue_reading; --count)
  370. continue_reading = ReadClassData(i->second, nullptr);
  371. }
  372. else
  373. {
  374. continue_reading = false;
  375. Trace("ObjectStreamIn: Found instance of unknown class %s.", inClassName);
  376. }
  377. }
  378. else
  379. {
  380. for (; count > 0 && continue_reading; --count)
  381. {
  382. switch (inDataType)
  383. {
  384. case EOSDataType::Pointer:
  385. {
  386. Identifier temporary;
  387. continue_reading = ReadIdentifier(temporary);
  388. break;
  389. }
  390. case EOSDataType::T_uint8:
  391. {
  392. uint8 temporary;
  393. continue_reading = ReadPrimitiveData(temporary);
  394. break;
  395. }
  396. case EOSDataType::T_uint16:
  397. {
  398. uint16 temporary;
  399. continue_reading = ReadPrimitiveData(temporary);
  400. break;
  401. }
  402. case EOSDataType::T_int:
  403. {
  404. int temporary;
  405. continue_reading = ReadPrimitiveData(temporary);
  406. break;
  407. }
  408. case EOSDataType::T_uint32:
  409. {
  410. uint32 temporary;
  411. continue_reading = ReadPrimitiveData(temporary);
  412. break;
  413. }
  414. case EOSDataType::T_uint64:
  415. {
  416. uint64 temporary;
  417. continue_reading = ReadPrimitiveData(temporary);
  418. break;
  419. }
  420. case EOSDataType::T_float:
  421. {
  422. float temporary;
  423. continue_reading = ReadPrimitiveData(temporary);
  424. break;
  425. }
  426. case EOSDataType::T_bool:
  427. {
  428. bool temporary;
  429. continue_reading = ReadPrimitiveData(temporary);
  430. break;
  431. }
  432. case EOSDataType::T_string:
  433. {
  434. string temporary;
  435. continue_reading = ReadPrimitiveData(temporary);
  436. break;
  437. }
  438. case EOSDataType::T_Float3:
  439. {
  440. Float3 temporary;
  441. continue_reading = ReadPrimitiveData(temporary);
  442. break;
  443. }
  444. case EOSDataType::T_Vec3:
  445. {
  446. Vec3 temporary;
  447. continue_reading = ReadPrimitiveData(temporary);
  448. break;
  449. }
  450. case EOSDataType::T_Vec4:
  451. {
  452. Vec4 temporary;
  453. continue_reading = ReadPrimitiveData(temporary);
  454. break;
  455. }
  456. case EOSDataType::T_Quat:
  457. {
  458. Quat temporary;
  459. continue_reading = ReadPrimitiveData(temporary);
  460. break;
  461. }
  462. case EOSDataType::T_Mat44:
  463. {
  464. Mat44 temporary;
  465. continue_reading = ReadPrimitiveData(temporary);
  466. break;
  467. }
  468. case EOSDataType::Array:
  469. case EOSDataType::Object:
  470. case EOSDataType::Declare:
  471. case EOSDataType::Instance:
  472. case EOSDataType::Invalid:
  473. default:
  474. continue_reading = false;
  475. break;
  476. }
  477. }
  478. }
  479. }
  480. return continue_reading;
  481. }
  482. // Define macro to declare functions for a specific primitive type
  483. #define JPH_DECLARE_PRIMITIVE(name) \
  484. bool OSReadData(ObjectStreamIn &ioStream, name &outPrimitive) \
  485. { \
  486. return ioStream.ReadPrimitiveData(outPrimitive); \
  487. }
  488. // This file uses the JPH_DECLARE_PRIMITIVE macro to define all types
  489. #include <Jolt/ObjectStream/ObjectStreamTypes.h>
  490. JPH_NAMESPACE_END