memstream.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. /* -*- tab-width: 4; -*- */
  2. /* vi: set sw=2 ts=4 expandtab: */
  3. /*
  4. * Copyright 2010-2020 The Khronos Group Inc.
  5. * SPDX-License-Identifier: Apache-2.0
  6. */
  7. /**
  8. * @file
  9. * @~English
  10. *
  11. * @brief Implementation of ktxStream for memory.
  12. *
  13. * @author Maksim Kolesin, Under Development
  14. * @author Georg Kolling, Imagination Technology
  15. * @author Mark Callow, HI Corporation
  16. */
  17. #include <assert.h>
  18. #include <string.h>
  19. #include <stdlib.h>
  20. #include "ktx.h"
  21. #include "ktxint.h"
  22. #include "memstream.h"
  23. /**
  24. * @brief Default allocation size for a ktxMemStream.
  25. */
  26. #define KTX_MEM_DEFAULT_ALLOCATED_SIZE 256
  27. /**
  28. * @brief Structure to store information about data allocated for ktxMemStream.
  29. */
  30. struct ktxMem
  31. {
  32. const ktx_uint8_t* robytes;/*!< pointer to read-only data */
  33. ktx_uint8_t* bytes; /*!< pointer to rw data. */
  34. ktx_size_t alloc_size; /*!< allocated size of the memory block. */
  35. ktx_size_t used_size; /*!< bytes used. Effectively the write position. */
  36. ktx_off_t pos; /*!< read/write position. */
  37. };
  38. static KTX_error_code ktxMem_expand(ktxMem* pMem, const ktx_size_t size);
  39. /**
  40. * @brief Initialize a ktxMem struct for read-write.
  41. *
  42. * Memory for the stream data is allocated internally but the
  43. * caller is responsible for freeing the memory. A pointer to
  44. * the memory can be obtained with ktxMem_getdata().
  45. *
  46. * @sa ktxMem_getdata.
  47. *
  48. * @param [in] pMem pointer to the @c ktxMem to initialize.
  49. */
  50. static KTX_error_code
  51. ktxMem_construct(ktxMem* pMem)
  52. {
  53. pMem->pos = 0;
  54. pMem->alloc_size = 0;
  55. pMem->robytes = 0;
  56. pMem->bytes = 0;
  57. pMem->used_size = 0;
  58. return ktxMem_expand(pMem, KTX_MEM_DEFAULT_ALLOCATED_SIZE);
  59. }
  60. /**
  61. * @brief Create & initialize a ktxMem struct for read-write.
  62. *
  63. * @sa ktxMem_construct.
  64. *
  65. * @param [in,out] ppMem pointer to the location in which to return
  66. * a pointer to the newly created @c ktxMem.
  67. *
  68. * @return KTX_SUCCESS on success, KTX_OUT_OF_MEMORY on error.
  69. *
  70. * @exception KTX_OUT_OF_MEMORY System failed to allocate sufficient pMemory.
  71. */
  72. static KTX_error_code
  73. ktxMem_create(ktxMem** ppMem)
  74. {
  75. ktxMem* pNewMem = (ktxMem*)malloc(sizeof(ktxMem));
  76. if (pNewMem) {
  77. KTX_error_code result = ktxMem_construct(pNewMem);
  78. if (result == KTX_SUCCESS)
  79. *ppMem = pNewMem;
  80. return result;
  81. }
  82. else {
  83. return KTX_OUT_OF_MEMORY;
  84. }
  85. }
  86. /**
  87. * @brief Initialize a ktxMem struct for read-only.
  88. *
  89. * @param [in] pMem pointer to the @c ktxMem to initialize.
  90. * @param [in] bytes pointer to the data to be read.
  91. * @param [in] numBytes number of bytes of data.
  92. */
  93. static void
  94. ktxMem_construct_ro(ktxMem* pMem, const void* bytes, ktx_size_t numBytes)
  95. {
  96. pMem->pos = 0;
  97. pMem->robytes = bytes;
  98. pMem->bytes = 0;
  99. pMem->used_size = numBytes;
  100. pMem->alloc_size = numBytes;
  101. }
  102. /**
  103. * @brief Create & initialize a ktxMem struct for read-only.
  104. *
  105. * @sa ktxMem_construct.
  106. *
  107. * @param [in,out] ppMem pointer to the location in which to return
  108. * a pointer to the newly created @c ktxMem.
  109. * @param [in] bytes pointer to the data to be read.
  110. * @param [in] numBytes number of bytes of data.
  111. *
  112. * @return KTX_SUCCESS on success, KTX_OUT_OF_MEMORY on error.
  113. *
  114. * @exception KTX_OUT_OF_MEMORY System failed to allocate sufficient pMemory.
  115. */
  116. static KTX_error_code
  117. ktxMem_create_ro(ktxMem** ppMem, const void* bytes, ktx_size_t numBytes)
  118. {
  119. ktxMem* pNewMem = (ktxMem*)malloc(sizeof(ktxMem));
  120. if (pNewMem) {
  121. ktxMem_construct_ro(pNewMem, bytes, numBytes);
  122. *ppMem = pNewMem;
  123. return KTX_SUCCESS;
  124. }
  125. else {
  126. return KTX_OUT_OF_MEMORY;
  127. }
  128. }
  129. /*
  130. * ktxMem_destruct not needed as ktxMem_construct caller is reponsible
  131. * for freeing the data written.
  132. */
  133. /**
  134. * @brief Free the memory of a struct ktxMem.
  135. *
  136. * @param pMem pointer to ktxMem to free.
  137. */
  138. static void
  139. ktxMem_destroy(ktxMem* pMem, ktx_bool_t freeData)
  140. {
  141. assert(pMem != NULL);
  142. if (freeData) {
  143. free(pMem->bytes);
  144. }
  145. free(pMem);
  146. }
  147. #ifdef KTXMEM_CLEAR_USED
  148. /**
  149. * @brief Clear the data of a memory stream.
  150. *
  151. * @param pMem pointer to ktxMem to clear.
  152. */
  153. static void
  154. ktxMem_clear(ktxMem* pMem)
  155. {
  156. assert(pMem != NULL);
  157. memset(pMem, 0, sizeof(ktxMem));
  158. }
  159. #endif
  160. /**
  161. * @~English
  162. * @brief Expand a ktxMem to fit to a new size.
  163. *
  164. * @param [in] pMem pointer to ktxMem struct to expand.
  165. * @param [in] newsize minimum new size required.
  166. *
  167. * @return KTX_SUCCESS on success, KTX_OUT_OF_MEMORY on error.
  168. *
  169. * @exception KTX_OUT_OF_MEMORY System failed to allocate sufficient pMemory.
  170. */
  171. static KTX_error_code
  172. ktxMem_expand(ktxMem *pMem, const ktx_size_t newsize)
  173. {
  174. ktx_size_t new_alloc_size;
  175. assert(pMem != NULL && newsize != 0);
  176. new_alloc_size = pMem->alloc_size == 0 ?
  177. KTX_MEM_DEFAULT_ALLOCATED_SIZE : pMem->alloc_size;
  178. while (new_alloc_size < newsize) {
  179. ktx_size_t alloc_size = new_alloc_size;
  180. new_alloc_size <<= 1;
  181. if (new_alloc_size < alloc_size) {
  182. /* Overflow. Set to maximum size. newsize can't be larger. */
  183. new_alloc_size = (ktx_size_t)-1L;
  184. }
  185. }
  186. if (new_alloc_size == pMem->alloc_size)
  187. return KTX_SUCCESS;
  188. if (!pMem->bytes)
  189. pMem->bytes = (ktx_uint8_t*)malloc(new_alloc_size);
  190. else
  191. pMem->bytes = (ktx_uint8_t*)realloc(pMem->bytes, new_alloc_size);
  192. if (!pMem->bytes)
  193. {
  194. pMem->alloc_size = 0;
  195. pMem->used_size = 0;
  196. return KTX_OUT_OF_MEMORY;
  197. }
  198. pMem->alloc_size = new_alloc_size;
  199. return KTX_SUCCESS;
  200. }
  201. /**
  202. * @~English
  203. * @brief Read bytes from a ktxMemStream.
  204. *
  205. * @param [in] str pointer to ktxMem struct, converted to a void*, that
  206. * specifies an input stream.
  207. * @param [in,out] dst pointer to memory where to copy read bytes.
  208. * @param [in,out] count pointer to number of bytes to read.
  209. *
  210. * @return KTX_SUCCESS on success, KTX_INVALID_VALUE on error.
  211. *
  212. * @exception KTX_INVALID_VALUE @p str or @p dst is @c NULL or @p str->data is
  213. * @c NULL.
  214. * @exception KTX_FILE_UNEXPECTED_EOF not enough data to satisfy the request.
  215. */
  216. static
  217. KTX_error_code ktxMemStream_read(ktxStream* str, void* dst, const ktx_size_t count)
  218. {
  219. ktxMem* mem;
  220. ktx_off_t newpos;
  221. const ktx_uint8_t* bytes;
  222. if (!str || (mem = str->data.mem)== 0)
  223. return KTX_INVALID_VALUE;
  224. newpos = mem->pos + count;
  225. /* The first clause checks for overflow. */
  226. if (newpos < mem->pos || (ktx_uint32_t)newpos > mem->used_size)
  227. return KTX_FILE_UNEXPECTED_EOF;
  228. bytes = mem->robytes ? mem->robytes : mem->bytes;
  229. memcpy(dst, bytes + mem->pos, count);
  230. mem->pos = newpos;
  231. return KTX_SUCCESS;
  232. }
  233. /**
  234. * @~English
  235. * @brief Skip bytes in a ktxMemStream.
  236. *
  237. * @param [in] str pointer to the ktxStream on which to operate.
  238. * @param [in] count number of bytes to skip.
  239. *
  240. * @return KTX_SUCCESS on success, KTX_INVALID_VALUE on error.
  241. *
  242. * @exception KTX_INVALID_VALUE @p str or @p mem is @c NULL or sufficient
  243. * data is not available in ktxMem.
  244. * @exception KTX_FILE_UNEXPECTED_EOF not enough data to satisfy the request.
  245. */
  246. static
  247. KTX_error_code ktxMemStream_skip(ktxStream* str, const ktx_size_t count)
  248. {
  249. ktxMem* mem;
  250. ktx_off_t newpos;
  251. if (!str || (mem = str->data.mem) == 0)
  252. return KTX_INVALID_VALUE;
  253. newpos = mem->pos + count;
  254. /* The first clause checks for overflow. */
  255. if (newpos < mem->pos || (ktx_uint32_t)newpos > mem->used_size)
  256. return KTX_FILE_UNEXPECTED_EOF;
  257. mem->pos = newpos;
  258. return KTX_SUCCESS;
  259. }
  260. /**
  261. * @~English
  262. * @brief Write bytes to a ktxMemStream.
  263. *
  264. * @param [out] str pointer to the ktxStream that specifies the destination.
  265. * @param [in] src pointer to the array of elements to be written,
  266. * converted to a const void*.
  267. * @param [in] size size in bytes of each element to be written.
  268. * @param [in] count number of elements, each one with a @p size of size
  269. * bytes.
  270. *
  271. * @return KTX_SUCCESS on success, other KTX_* enum values on error.
  272. *
  273. * @exception KTX_FILE_OVERFLOW write would result in file exceeding the
  274. * maximum permissible size.
  275. * @exception KTX_INVALID_OPERATION @p str is a read-only stream.
  276. * @exception KTX_INVALID_VALUE @p dst is @c NULL or @p mem is @c NULL.
  277. * @exception KTX_OUT_OF_MEMORY See ktxMem_expand() for causes.
  278. */
  279. static
  280. KTX_error_code ktxMemStream_write(ktxStream* str, const void* src,
  281. const ktx_size_t size, const ktx_size_t count)
  282. {
  283. ktxMem* mem;
  284. KTX_error_code rc = KTX_SUCCESS;
  285. ktx_size_t new_size;
  286. if (!str || (mem = str->data.mem) == 0)
  287. return KTX_INVALID_VALUE;
  288. if (mem->robytes)
  289. return KTX_INVALID_OPERATION; /* read-only */
  290. new_size = mem->pos + (size*count);
  291. //if (new_size < mem->used_size)
  292. if ((ktx_off_t)new_size < mem->pos)
  293. return KTX_FILE_OVERFLOW;
  294. if (mem->alloc_size < new_size) {
  295. rc = ktxMem_expand(mem, new_size);
  296. if (rc != KTX_SUCCESS)
  297. return rc;
  298. }
  299. memcpy(mem->bytes + mem->pos, src, size*count);
  300. mem->pos += size*count;
  301. if (mem->pos > (ktx_off_t)mem->used_size)
  302. mem->used_size = mem->pos;
  303. return KTX_SUCCESS;
  304. }
  305. /**
  306. * @~English
  307. * @brief Get the current read/write position in a ktxMemStream.
  308. *
  309. * @param [in] str pointer to the ktxStream to query.
  310. * @param [in,out] off pointer to variable to receive the offset value.
  311. *
  312. * @return KTX_SUCCESS on success, other KTX_* enum values on error.
  313. *
  314. * @exception KTX_INVALID_VALUE @p str or @p pos is @c NULL.
  315. */
  316. static
  317. KTX_error_code ktxMemStream_getpos(ktxStream* str, ktx_off_t* const pos)
  318. {
  319. if (!str || !pos)
  320. return KTX_INVALID_VALUE;
  321. assert(str->type == eStreamTypeMemory);
  322. *pos = str->data.mem->pos;
  323. return KTX_SUCCESS;
  324. }
  325. /**
  326. * @~English
  327. * @brief Set the current read/write position in a ktxMemStream.
  328. *
  329. * Offset of 0 is the start of the file.
  330. *
  331. * @param [in] str pointer to the ktxStream whose r/w position is to be set.
  332. * @param [in] off pointer to the offset value to set.
  333. *
  334. * @return KTX_SUCCESS on success, other KTX_* enum values on error.
  335. *
  336. * @exception KTX_INVALID_VALUE @p str is @c NULL.
  337. * @exception KTX_INVALID_OPERATION @p pos > size of the allocated memory.
  338. */
  339. static
  340. KTX_error_code ktxMemStream_setpos(ktxStream* str, ktx_off_t pos)
  341. {
  342. if (!str)
  343. return KTX_INVALID_VALUE;
  344. assert(str->type == eStreamTypeMemory);
  345. if (pos > (ktx_off_t)str->data.mem->alloc_size)
  346. return KTX_INVALID_OPERATION;
  347. str->data.mem->pos = pos;
  348. return KTX_SUCCESS;
  349. }
  350. /**
  351. * @~English
  352. * @brief Get a pointer to a ktxMemStream's data.
  353. *
  354. * Gets a pointer to data that has been written to the stream. Returned
  355. * pointer will be 0 if stream is read-only.
  356. *
  357. * @param [in] str pointer to the ktxStream whose data pointer is to
  358. * be queried.
  359. * @param [in,out] ppBytes pointer to a variable in which the data pointer
  360. * will be written.
  361. *
  362. * @return KTX_SUCCESS on success, other KTX_* enum values on error.
  363. *
  364. * @exception KTX_INVALID_VALUE @p str or @p ppBytes is @c NULL.
  365. */
  366. KTX_error_code ktxMemStream_getdata(ktxStream* str, ktx_uint8_t** ppBytes)
  367. {
  368. if (!str || !ppBytes)
  369. return KTX_INVALID_VALUE;
  370. assert(str->type == eStreamTypeMemory);
  371. *ppBytes = str->data.mem->bytes;
  372. return KTX_SUCCESS;
  373. }
  374. /**
  375. * @~English
  376. * @brief Get the size of a ktxMemStream in bytes.
  377. *
  378. * @param [in] str pointer to the ktxStream whose size is to be queried.
  379. * @param [in,out] size pointer to a variable in which size will be written.
  380. *
  381. * @return KTX_SUCCESS on success, other KTX_* enum values on error.
  382. *
  383. * @exception KTX_INVALID_VALUE @p str or @p pSize is @c NULL.
  384. */
  385. static
  386. KTX_error_code ktxMemStream_getsize(ktxStream* str, ktx_size_t* pSize)
  387. {
  388. if (!str || !pSize)
  389. return KTX_INVALID_VALUE;
  390. assert(str->type == eStreamTypeMemory);
  391. *pSize = str->data.mem->used_size;
  392. return KTX_SUCCESS;
  393. }
  394. /**
  395. * @~English
  396. * @brief Setup ktxMemStream function pointers.
  397. */
  398. void
  399. ktxMemStream_setup(ktxStream* str)
  400. {
  401. str->type = eStreamTypeMemory;
  402. str->read = ktxMemStream_read;
  403. str->skip = ktxMemStream_skip;
  404. str->write = ktxMemStream_write;
  405. str->getpos = ktxMemStream_getpos;
  406. str->setpos = ktxMemStream_setpos;
  407. str->getsize = ktxMemStream_getsize;
  408. str->destruct = ktxMemStream_destruct;
  409. }
  410. /**
  411. * @~English
  412. * @brief Initialize a read-write ktxMemStream.
  413. *
  414. * Memory is allocated as data is written. The caller of this is
  415. * responsible for freeing this memory unless @a freeOnDestruct
  416. * is not KTX_FALSE.
  417. *
  418. * @param [in] str pointer to a ktxStream struct to initialize.
  419. * @param [in] freeOnDestruct If not KTX_FALSE memory holding the data will
  420. * be freed by the destructor.
  421. *
  422. * @return KTX_SUCCESS on success, other KTX_* enum values on error.
  423. *
  424. * @exception KTX_INVALID_VALUE @p str is @c NULL.
  425. * @exception KTX_OUT_OF_MEMORY system failed to allocate sufficient memory.
  426. */
  427. KTX_error_code ktxMemStream_construct(ktxStream* str,
  428. ktx_bool_t freeOnDestruct)
  429. {
  430. ktxMem* mem;
  431. KTX_error_code result = KTX_SUCCESS;
  432. if (!str)
  433. return KTX_INVALID_VALUE;
  434. result = ktxMem_create(&mem);
  435. if (KTX_SUCCESS == result) {
  436. str->data.mem = mem;
  437. ktxMemStream_setup(str);
  438. str->closeOnDestruct = freeOnDestruct;
  439. }
  440. return result;
  441. }
  442. /**
  443. * @~English
  444. * @brief Initialize a read-only ktxMemStream.
  445. *
  446. * @param [in] str pointer to a ktxStream struct to initialize.
  447. * @param [in] bytes pointer to an array of bytes containing the data.
  448. * @param [in] numBytes size of array of data for ktxMemStream.
  449. *
  450. * @return KTX_SUCCESS on success, other KTX_* enum values on error.
  451. *
  452. * @exception KTX_INVALID_VALUE @p str or @p mem is @c NULL or @p numBytes
  453. * is 0.
  454. * or @p size is less than 0.
  455. * @exception KTX_OUT_OF_MEMORY system failed to allocate sufficient memory.
  456. */
  457. KTX_error_code ktxMemStream_construct_ro(ktxStream* str,
  458. const ktx_uint8_t* bytes,
  459. const ktx_size_t numBytes)
  460. {
  461. ktxMem* mem;
  462. KTX_error_code result = KTX_SUCCESS;
  463. if (!str || !bytes || numBytes == 0)
  464. return KTX_INVALID_VALUE;
  465. result = ktxMem_create_ro(&mem, bytes, numBytes);
  466. if (KTX_SUCCESS == result) {
  467. str->data.mem = mem;
  468. ktxMemStream_setup(str);
  469. str->closeOnDestruct = KTX_FALSE;
  470. }
  471. return result;
  472. }
  473. /**
  474. * @~English
  475. * @brief Free the memory used by a ktxMemStream.
  476. *
  477. * This only frees the memory used to store the data written to the stream,
  478. * if the @c freeOnDestruct parameter to ktxMemStream_construct() was not
  479. * @c KTX_FALSE. Otherwise it is the responsibility of the caller of
  480. * ktxMemStream_construct() and a pointer to this memory should be retrieved
  481. * using ktxMemStream_getdata() before calling this function.
  482. *
  483. * @sa ktxMemStream_construct, ktxMemStream_getdata.
  484. *
  485. * @param [in] str pointer to the ktxStream whose memory is
  486. * to be freed.
  487. */
  488. void
  489. ktxMemStream_destruct(ktxStream* str)
  490. {
  491. assert(str && str->type == eStreamTypeMemory);
  492. ktxMem_destroy(str->data.mem, str->closeOnDestruct);
  493. str->data.mem = NULL;
  494. }