Serializer.inl.h 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. // Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Util/Serializer.h>
  6. namespace anki {
  7. namespace detail {
  8. class alignas(ANKI_SAFE_ALIGNMENT) BinarySerializerHeader
  9. {
  10. public:
  11. Array<U8, 8> m_magic;
  12. PtrSize m_dataSize;
  13. PtrSize m_pointerArrayFilePosition; ///< Points to an array of file positions that contain pointers.
  14. PtrSize m_pointerCount; ///< The size of the above.
  15. };
  16. inline constexpr const char* kBinarySerializerMagic = "ANKIBIN1";
  17. } // end namespace detail
  18. template<typename T>
  19. Error BinarySerializer::serializeInternal(const T& x, BaseMemoryPool& tmpPool, File& file)
  20. {
  21. checkStruct<T>();
  22. // Misc
  23. m_file = &file;
  24. ANKI_ASSERT(m_file->tell() == 0);
  25. m_err = Error::kNone;
  26. m_pool = &tmpPool;
  27. m_pointerFilePositions = DynamicArray<PointerInfo, Pool>(m_pool);
  28. m_structureFilePos = DynamicArray<PtrSize, Pool>(m_pool);
  29. // Write the empty header (will be filled later)
  30. detail::BinarySerializerHeader header = {};
  31. const PtrSize headerFilePos = m_file->tell();
  32. ANKI_CHECK(m_file->write(&header, sizeof(header)));
  33. // Set EOF file pos
  34. const PtrSize dataFilePos = headerFilePos + sizeof(header);
  35. ANKI_ASSERT(isAligned(ANKI_SAFE_ALIGNMENT, dataFilePos));
  36. m_eofPos = dataFilePos + sizeof(T);
  37. m_beginOfDataFilePos = dataFilePos;
  38. // Finaly, serialize
  39. ANKI_CHECK(m_file->write(&x, sizeof(T)));
  40. m_structureFilePos.emplaceBack(dataFilePos);
  41. doValue("root", 0, x);
  42. m_structureFilePos.popBack();
  43. if(m_err)
  44. {
  45. return m_err;
  46. }
  47. // Write all pointers. Do that now and not while writing the actual shader in order to avoid the file seeks
  48. DynamicArray<PtrSize, Pool> pointerFilePositions(m_pool);
  49. for(const PointerInfo& pointer : m_pointerFilePositions)
  50. {
  51. ANKI_CHECK(m_file->seek(pointer.m_filePos, FileSeekOrigin::kBeginning));
  52. ANKI_CHECK(m_file->write(&pointer.m_value, sizeof(pointer.m_value)));
  53. const PtrSize offsetAfterHeader = pointer.m_filePos - m_beginOfDataFilePos;
  54. ANKI_ASSERT(offsetAfterHeader + sizeof(void*) <= m_eofPos - m_beginOfDataFilePos);
  55. pointerFilePositions.emplaceBack(offsetAfterHeader);
  56. }
  57. // Write the pointer offsets
  58. if(pointerFilePositions.getSize() > 0)
  59. {
  60. ANKI_CHECK(m_file->seek(m_eofPos, FileSeekOrigin::kBeginning));
  61. ANKI_CHECK(m_file->write(&pointerFilePositions[0], pointerFilePositions.getSizeInBytes()));
  62. header.m_pointerCount = pointerFilePositions.getSize();
  63. header.m_pointerArrayFilePosition = m_eofPos;
  64. }
  65. // Write the header
  66. memcpy(&header.m_magic[0], detail::kBinarySerializerMagic, sizeof(header.m_magic));
  67. header.m_dataSize = m_eofPos - dataFilePos;
  68. ANKI_CHECK(m_file->seek(headerFilePos, FileSeekOrigin::kBeginning));
  69. ANKI_CHECK(m_file->write(&header, sizeof(header)));
  70. // Done
  71. m_file = nullptr;
  72. m_pointerFilePositions.destroy();
  73. m_pool = nullptr;
  74. return Error::kNone;
  75. }
  76. template<typename T>
  77. Error BinarySerializer::doArrayComplexType(const T* arr, PtrSize size, PtrSize memberOffset)
  78. {
  79. ANKI_ASSERT(arr && size > 0);
  80. check();
  81. checkStruct<T>();
  82. // Serialize pointers
  83. PtrSize structFilePos = m_structureFilePos.getBack() + memberOffset;
  84. for(PtrSize i = 0; i < size; ++i)
  85. {
  86. m_structureFilePos.emplaceBack(structFilePos);
  87. // Serialize the pointers
  88. SerializeFunctor<T>()(arr[i], *this);
  89. ANKI_CHECK(m_err);
  90. // Advance file pos
  91. m_structureFilePos.popBack();
  92. structFilePos += sizeof(T);
  93. }
  94. check();
  95. return Error::kNone;
  96. }
  97. template<typename T>
  98. Error BinarySerializer::doDynamicArrayComplexType(const T* arr, PtrSize size, PtrSize memberOffset)
  99. {
  100. check();
  101. checkStruct<T>();
  102. if(size == 0)
  103. {
  104. ANKI_ASSERT(arr == nullptr);
  105. // Do nothing
  106. }
  107. else
  108. {
  109. ANKI_ASSERT(arr);
  110. // Move file pos to the end of the file (allocate space)
  111. PtrSize arrayFilePos = getAlignedRoundUp(alignof(T), m_eofPos);
  112. m_eofPos = arrayFilePos + size * sizeof(T);
  113. // Store the pointer for later
  114. const PtrSize structFilePos = m_structureFilePos.getBack();
  115. PointerInfo pinfo;
  116. pinfo.m_filePos = structFilePos + memberOffset;
  117. pinfo.m_value = arrayFilePos - m_beginOfDataFilePos;
  118. m_pointerFilePositions.emplaceBack(pinfo);
  119. // Write the structures
  120. ANKI_CHECK(m_file->seek(arrayFilePos, FileSeekOrigin::kBeginning));
  121. ANKI_CHECK(m_file->write(&arr[0], sizeof(T) * size));
  122. // Basically serialize pointers
  123. for(PtrSize i = 0; i < size && !m_err; ++i)
  124. {
  125. m_structureFilePos.emplaceBack(arrayFilePos);
  126. SerializeFunctor<T>()(arr[i], *this);
  127. m_structureFilePos.popBack();
  128. arrayFilePos += sizeof(T);
  129. }
  130. ANKI_CHECK(m_err);
  131. }
  132. check();
  133. return Error::kNone;
  134. }
  135. template<typename T, typename TFile>
  136. Error BinaryDeserializer::deserialize(T*& x, BaseMemoryPool& pool, TFile& file)
  137. {
  138. x = nullptr;
  139. detail::BinarySerializerHeader header;
  140. ANKI_CHECK(file.read(&header, sizeof(header)));
  141. const PtrSize dataFilePos = sizeof(header);
  142. // Sanity checks
  143. {
  144. if(memcmp(&header.m_magic[0], detail::kBinarySerializerMagic, 8) != 0)
  145. {
  146. ANKI_UTIL_LOGE("Wrong magic work in header");
  147. return Error::kUserData;
  148. }
  149. if(header.m_dataSize < sizeof(T))
  150. {
  151. ANKI_UTIL_LOGE("Wrong data size");
  152. return Error::kUserData;
  153. }
  154. const PtrSize expectedSizeAfterHeader = header.m_dataSize + header.m_pointerCount * sizeof(void*);
  155. const PtrSize actualSizeAfterHeader = file.getSize() - dataFilePos;
  156. if(expectedSizeAfterHeader > actualSizeAfterHeader)
  157. {
  158. ANKI_UTIL_LOGE("File size doesn't match expectations");
  159. return Error::kUserData;
  160. }
  161. }
  162. // Allocate & read data
  163. U8* const baseAddress = static_cast<U8*>(pool.allocate(header.m_dataSize, ANKI_SAFE_ALIGNMENT));
  164. ANKI_CHECK(file.read(baseAddress, header.m_dataSize));
  165. // Fix pointers
  166. if(header.m_pointerCount)
  167. {
  168. ANKI_CHECK(file.seek(header.m_pointerArrayFilePosition, FileSeekOrigin::kBeginning));
  169. for(PtrSize i = 0; i < header.m_pointerCount; ++i)
  170. {
  171. // Read the location of the pointer
  172. PtrSize offsetFromBeginOfData;
  173. ANKI_CHECK(file.read(&offsetFromBeginOfData, sizeof(offsetFromBeginOfData)));
  174. if(offsetFromBeginOfData >= header.m_dataSize)
  175. {
  176. ANKI_UTIL_LOGE("Corrupt pointer");
  177. return Error::kUserData;
  178. }
  179. // Add to the location the actual base address
  180. U8* ptrLocation = baseAddress + offsetFromBeginOfData;
  181. PtrSize& ptrValue = *reinterpret_cast<PtrSize*>(ptrLocation);
  182. if(ptrValue >= header.m_dataSize)
  183. {
  184. ANKI_UTIL_LOGE("Corrupt pointer");
  185. return Error::kUserData;
  186. }
  187. ptrValue += ptrToNumber(baseAddress);
  188. }
  189. }
  190. // Done
  191. x = reinterpret_cast<T*>(baseAddress);
  192. return Error::kNone;
  193. }
  194. } // end namespace anki