mz_strm_buf.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. /* mz_strm_buf.c -- Stream for buffering reads/writes
  2. part of the minizip-ng project
  3. This version of ioapi is designed to buffer IO.
  4. Copyright (C) 2010-2021 Nathan Moinvaziri
  5. https://github.com/zlib-ng/minizip-ng
  6. This program is distributed under the terms of the same license as zlib.
  7. See the accompanying LICENSE file for the full text of the license.
  8. */
  9. #include "mz.h"
  10. #include "mz_strm.h"
  11. #include "mz_strm_buf.h"
  12. /***************************************************************************/
  13. static mz_stream_vtbl mz_stream_buffered_vtbl = {
  14. mz_stream_buffered_open,
  15. mz_stream_buffered_is_open,
  16. mz_stream_buffered_read,
  17. mz_stream_buffered_write,
  18. mz_stream_buffered_tell,
  19. mz_stream_buffered_seek,
  20. mz_stream_buffered_close,
  21. mz_stream_buffered_error,
  22. mz_stream_buffered_create,
  23. mz_stream_buffered_delete,
  24. NULL,
  25. NULL
  26. };
  27. /***************************************************************************/
  28. typedef struct mz_stream_buffered_s {
  29. mz_stream stream;
  30. int32_t error;
  31. char readbuf[INT16_MAX];
  32. int32_t readbuf_len;
  33. int32_t readbuf_pos;
  34. int32_t readbuf_hits;
  35. int32_t readbuf_misses;
  36. char writebuf[INT16_MAX];
  37. int32_t writebuf_len;
  38. int32_t writebuf_pos;
  39. int32_t writebuf_hits;
  40. int32_t writebuf_misses;
  41. int64_t position;
  42. } mz_stream_buffered;
  43. /***************************************************************************/
  44. #if 0
  45. # define mz_stream_buffered_print printf
  46. #else
  47. # define mz_stream_buffered_print(fmt,...)
  48. #endif
  49. /***************************************************************************/
  50. static int32_t mz_stream_buffered_reset(void *stream) {
  51. mz_stream_buffered *buffered = (mz_stream_buffered *)stream;
  52. buffered->readbuf_len = 0;
  53. buffered->readbuf_pos = 0;
  54. buffered->writebuf_len = 0;
  55. buffered->writebuf_pos = 0;
  56. buffered->position = 0;
  57. return MZ_OK;
  58. }
  59. int32_t mz_stream_buffered_open(void *stream, const char *path, int32_t mode) {
  60. mz_stream_buffered *buffered = (mz_stream_buffered *)stream;
  61. mz_stream_buffered_print("Buffered - Open (mode %" PRId32 ")\n", mode);
  62. mz_stream_buffered_reset(buffered);
  63. return mz_stream_open(buffered->stream.base, path, mode);
  64. }
  65. int32_t mz_stream_buffered_is_open(void *stream) {
  66. mz_stream_buffered *buffered = (mz_stream_buffered *)stream;
  67. return mz_stream_is_open(buffered->stream.base);
  68. }
  69. static int32_t mz_stream_buffered_flush(void *stream, int32_t *written) {
  70. mz_stream_buffered *buffered = (mz_stream_buffered *)stream;
  71. int32_t total_bytes_written = 0;
  72. int32_t bytes_to_write = buffered->writebuf_len;
  73. int32_t bytes_left_to_write = buffered->writebuf_len;
  74. int32_t bytes_written = 0;
  75. *written = 0;
  76. while (bytes_left_to_write > 0) {
  77. bytes_written = mz_stream_write(buffered->stream.base,
  78. buffered->writebuf + (bytes_to_write - bytes_left_to_write), bytes_left_to_write);
  79. if (bytes_written != bytes_left_to_write)
  80. return MZ_WRITE_ERROR;
  81. buffered->writebuf_misses += 1;
  82. mz_stream_buffered_print("Buffered - Write flush (%" PRId32 ":%" PRId32 " len %" PRId32 ")\n",
  83. bytes_to_write, bytes_left_to_write, buffered->writebuf_len);
  84. total_bytes_written += bytes_written;
  85. bytes_left_to_write -= bytes_written;
  86. buffered->position += bytes_written;
  87. }
  88. buffered->writebuf_len = 0;
  89. buffered->writebuf_pos = 0;
  90. *written = total_bytes_written;
  91. return MZ_OK;
  92. }
  93. int32_t mz_stream_buffered_read(void *stream, void *buf, int32_t size) {
  94. mz_stream_buffered *buffered = (mz_stream_buffered *)stream;
  95. int32_t buf_len = 0;
  96. int32_t bytes_to_read = 0;
  97. int32_t bytes_to_copy = 0;
  98. int32_t bytes_left_to_read = size;
  99. int32_t bytes_read = 0;
  100. int32_t bytes_flushed = 0;
  101. mz_stream_buffered_print("Buffered - Read (size %" PRId32 " pos %" PRId64 ")\n", size, buffered->position);
  102. if (buffered->writebuf_len > 0) {
  103. int64_t position = buffered->position + buffered->writebuf_pos
  104. mz_stream_buffered_print("Buffered - Switch from write to read, flushing (pos %" PRId64 ")\n", position);
  105. mz_stream_buffered_flush(stream, &bytes_flushed);
  106. mz_stream_buffered_seek(stream, position, MZ_SEEK_SET);
  107. }
  108. while (bytes_left_to_read > 0) {
  109. if ((buffered->readbuf_len == 0) || (buffered->readbuf_pos == buffered->readbuf_len)) {
  110. if (buffered->readbuf_len == sizeof(buffered->readbuf)) {
  111. buffered->readbuf_pos = 0;
  112. buffered->readbuf_len = 0;
  113. }
  114. bytes_to_read = (int32_t)sizeof(buffered->readbuf) - (buffered->readbuf_len - buffered->readbuf_pos);
  115. bytes_read = mz_stream_read(buffered->stream.base, buffered->readbuf + buffered->readbuf_pos, bytes_to_read);
  116. if (bytes_read < 0)
  117. return bytes_read;
  118. buffered->readbuf_misses += 1;
  119. buffered->readbuf_len += bytes_read;
  120. buffered->position += bytes_read;
  121. mz_stream_buffered_print("Buffered - Filled (read %" PRId32 "/%" PRId32 " buf %" PRId32 ":%" PRId32 " pos %" PRId64 ")\n",
  122. bytes_read, bytes_to_read, buffered->readbuf_pos, buffered->readbuf_len, buffered->position);
  123. if (bytes_read == 0)
  124. break;
  125. }
  126. if ((buffered->readbuf_len - buffered->readbuf_pos) > 0) {
  127. bytes_to_copy = buffered->readbuf_len - buffered->readbuf_pos;
  128. if (bytes_to_copy > bytes_left_to_read)
  129. bytes_to_copy = bytes_left_to_read;
  130. memcpy((char *)buf + buf_len, buffered->readbuf + buffered->readbuf_pos, bytes_to_copy);
  131. buf_len += bytes_to_copy;
  132. bytes_left_to_read -= bytes_to_copy;
  133. buffered->readbuf_hits += 1;
  134. buffered->readbuf_pos += bytes_to_copy;
  135. mz_stream_buffered_print("Buffered - Emptied (copied %" PRId32 " remaining %" PRId32 " buf %" PRId32 ":%" PRId32 " pos %" PRId64 ")\n",
  136. bytes_to_copy, bytes_left_to_read, buffered->readbuf_pos, buffered->readbuf_len, buffered->position);
  137. }
  138. }
  139. return size - bytes_left_to_read;
  140. }
  141. int32_t mz_stream_buffered_write(void *stream, const void *buf, int32_t size) {
  142. mz_stream_buffered *buffered = (mz_stream_buffered *)stream;
  143. int32_t bytes_to_write = size;
  144. int32_t bytes_left_to_write = size;
  145. int32_t bytes_to_copy = 0;
  146. int32_t bytes_used = 0;
  147. int32_t bytes_flushed = 0;
  148. int32_t err = MZ_OK;
  149. mz_stream_buffered_print("Buffered - Write (size %" PRId32 " len %" PRId32 " pos %" PRId64 ")\n",
  150. size, buffered->writebuf_len, buffered->position);
  151. if (buffered->readbuf_len > 0) {
  152. buffered->position -= buffered->readbuf_len;
  153. buffered->position += buffered->readbuf_pos;
  154. buffered->readbuf_len = 0;
  155. buffered->readbuf_pos = 0;
  156. mz_stream_buffered_print("Buffered - Switch from read to write (pos %" PRId64 ")\n", buffered->position);
  157. err = mz_stream_seek(buffered->stream.base, buffered->position, MZ_SEEK_SET);
  158. if (err != MZ_OK)
  159. return err;
  160. }
  161. while (bytes_left_to_write > 0) {
  162. bytes_used = buffered->writebuf_len;
  163. if (bytes_used > buffered->writebuf_pos)
  164. bytes_used = buffered->writebuf_pos;
  165. bytes_to_copy = (int32_t)sizeof(buffered->writebuf) - bytes_used;
  166. if (bytes_to_copy > bytes_left_to_write)
  167. bytes_to_copy = bytes_left_to_write;
  168. if (bytes_to_copy == 0) {
  169. err = mz_stream_buffered_flush(stream, &bytes_flushed);
  170. if (err != MZ_OK)
  171. return err;
  172. if (bytes_flushed == 0)
  173. return 0;
  174. continue;
  175. }
  176. memcpy(buffered->writebuf + buffered->writebuf_pos,
  177. (const char *)buf + (bytes_to_write - bytes_left_to_write), bytes_to_copy);
  178. mz_stream_buffered_print("Buffered - Write copy (remaining %" PRId32 " write %" PRId32 ":%" PRId32 " len %" PRId32 ")\n",
  179. bytes_to_copy, bytes_to_write, bytes_left_to_write, buffered->writebuf_len);
  180. bytes_left_to_write -= bytes_to_copy;
  181. buffered->writebuf_pos += bytes_to_copy;
  182. buffered->writebuf_hits += 1;
  183. if (buffered->writebuf_pos > buffered->writebuf_len)
  184. buffered->writebuf_len += buffered->writebuf_pos - buffered->writebuf_len;
  185. }
  186. return size - bytes_left_to_write;
  187. }
  188. int64_t mz_stream_buffered_tell(void *stream) {
  189. mz_stream_buffered *buffered = (mz_stream_buffered *)stream;
  190. int64_t position = mz_stream_tell(buffered->stream.base);
  191. buffered->position = position;
  192. mz_stream_buffered_print("Buffered - Tell (pos %" PRId64 " readpos %" PRId32 " writepos %" PRId32 ")\n",
  193. buffered->position, buffered->readbuf_pos, buffered->writebuf_pos);
  194. if (buffered->readbuf_len > 0)
  195. position -= ((int64_t)buffered->readbuf_len - buffered->readbuf_pos);
  196. if (buffered->writebuf_len > 0)
  197. position += buffered->writebuf_pos;
  198. return position;
  199. }
  200. int32_t mz_stream_buffered_seek(void *stream, int64_t offset, int32_t origin) {
  201. mz_stream_buffered *buffered = (mz_stream_buffered *)stream;
  202. int32_t bytes_flushed = 0;
  203. int32_t err = MZ_OK;
  204. mz_stream_buffered_print("Buffered - Seek (origin %" PRId32 " offset %" PRId64 " pos %" PRId64 ")\n",
  205. origin, offset, buffered->position);
  206. switch (origin) {
  207. case MZ_SEEK_SET:
  208. if ((buffered->readbuf_len > 0) && (offset < buffered->position) &&
  209. (offset >= buffered->position - buffered->readbuf_len)) {
  210. buffered->readbuf_pos = (int32_t)(offset - (buffered->position - buffered->readbuf_len));
  211. return MZ_OK;
  212. }
  213. if (buffered->writebuf_len > 0) {
  214. if ((offset >= buffered->position) && (offset <= buffered->position + buffered->writebuf_len)) {
  215. buffered->writebuf_pos = (int32_t)(offset - buffered->position);
  216. return MZ_OK;
  217. }
  218. }
  219. err = mz_stream_buffered_flush(stream, &bytes_flushed);
  220. if (err != MZ_OK)
  221. return err;
  222. buffered->position = offset;
  223. break;
  224. case MZ_SEEK_CUR:
  225. if (buffered->readbuf_len > 0) {
  226. if (offset <= ((int64_t)buffered->readbuf_len - buffered->readbuf_pos)) {
  227. buffered->readbuf_pos += (uint32_t)offset;
  228. return MZ_OK;
  229. }
  230. offset -= ((int64_t)buffered->readbuf_len - buffered->readbuf_pos);
  231. buffered->position += offset;
  232. }
  233. if (buffered->writebuf_len > 0) {
  234. if (offset <= ((int64_t)buffered->writebuf_len - buffered->writebuf_pos)) {
  235. buffered->writebuf_pos += (uint32_t)offset;
  236. return MZ_OK;
  237. }
  238. /* offset -= (buffered->writebuf_len - buffered->writebuf_pos); */
  239. }
  240. err = mz_stream_buffered_flush(stream, &bytes_flushed);
  241. if (err != MZ_OK)
  242. return err;
  243. break;
  244. case MZ_SEEK_END:
  245. if (buffered->writebuf_len > 0) {
  246. buffered->writebuf_pos = buffered->writebuf_len;
  247. return MZ_OK;
  248. }
  249. break;
  250. }
  251. buffered->readbuf_len = 0;
  252. buffered->readbuf_pos = 0;
  253. buffered->writebuf_len = 0;
  254. buffered->writebuf_pos = 0;
  255. return mz_stream_seek(buffered->stream.base, offset, origin);
  256. }
  257. int32_t mz_stream_buffered_close(void *stream) {
  258. mz_stream_buffered *buffered = (mz_stream_buffered *)stream;
  259. int32_t bytes_flushed = 0;
  260. mz_stream_buffered_flush(stream, &bytes_flushed);
  261. mz_stream_buffered_print("Buffered - Close (flushed %" PRId32 ")\n", bytes_flushed);
  262. if (buffered->readbuf_hits + buffered->readbuf_misses > 0) {
  263. mz_stream_buffered_print("Buffered - Read efficiency %.02f%%\n",
  264. (buffered->readbuf_hits / ((float)buffered->readbuf_hits + buffered->readbuf_misses)) * 100);
  265. }
  266. if (buffered->writebuf_hits + buffered->writebuf_misses > 0) {
  267. mz_stream_buffered_print("Buffered - Write efficiency %.02f%%\n",
  268. (buffered->writebuf_hits / ((float)buffered->writebuf_hits + buffered->writebuf_misses)) * 100);
  269. }
  270. mz_stream_buffered_reset(buffered);
  271. return mz_stream_close(buffered->stream.base);
  272. }
  273. int32_t mz_stream_buffered_error(void *stream) {
  274. mz_stream_buffered *buffered = (mz_stream_buffered *)stream;
  275. return mz_stream_error(buffered->stream.base);
  276. }
  277. void *mz_stream_buffered_create(void **stream) {
  278. mz_stream_buffered *buffered = NULL;
  279. buffered = (mz_stream_buffered *)MZ_ALLOC(sizeof(mz_stream_buffered));
  280. if (buffered != NULL) {
  281. memset(buffered, 0, sizeof(mz_stream_buffered));
  282. buffered->stream.vtbl = &mz_stream_buffered_vtbl;
  283. }
  284. if (stream != NULL)
  285. *stream = buffered;
  286. return buffered;
  287. }
  288. void mz_stream_buffered_delete(void **stream) {
  289. mz_stream_buffered *buffered = NULL;
  290. if (stream == NULL)
  291. return;
  292. buffered = (mz_stream_buffered *)*stream;
  293. if (buffered != NULL)
  294. MZ_FREE(buffered);
  295. *stream = NULL;
  296. }
  297. void *mz_stream_buffered_get_interface(void) {
  298. return (void *)&mz_stream_buffered_vtbl;
  299. }