outqueue.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. /// \file outqueue.c
  4. /// \brief Output queue handling in multithreaded coding
  5. //
  6. // Author: Lasse Collin
  7. //
  8. // This file has been put into the public domain.
  9. // You can do whatever you want with this file.
  10. //
  11. ///////////////////////////////////////////////////////////////////////////////
  12. #include "outqueue.h"
  13. /// This is to ease integer overflow checking: We may allocate up to
  14. /// 2 * LZMA_THREADS_MAX buffers and we need some extra memory for other
  15. /// data structures (that's the second /2).
  16. #define BUF_SIZE_MAX (UINT64_MAX / LZMA_THREADS_MAX / 2 / 2)
  17. static lzma_ret
  18. get_options(uint64_t *bufs_alloc_size, uint32_t *bufs_count,
  19. uint64_t buf_size_max, uint32_t threads)
  20. {
  21. if (threads > LZMA_THREADS_MAX || buf_size_max > BUF_SIZE_MAX)
  22. return LZMA_OPTIONS_ERROR;
  23. // The number of buffers is twice the number of threads.
  24. // This wastes RAM but keeps the threads busy when buffers
  25. // finish out of order.
  26. //
  27. // NOTE: If this is changed, update BUF_SIZE_MAX too.
  28. *bufs_count = threads * 2;
  29. *bufs_alloc_size = *bufs_count * buf_size_max;
  30. return LZMA_OK;
  31. }
  32. extern uint64_t
  33. lzma_outq_memusage(uint64_t buf_size_max, uint32_t threads)
  34. {
  35. uint64_t bufs_alloc_size;
  36. uint32_t bufs_count;
  37. if (get_options(&bufs_alloc_size, &bufs_count, buf_size_max, threads)
  38. != LZMA_OK)
  39. return UINT64_MAX;
  40. return sizeof(lzma_outq) + bufs_count * sizeof(lzma_outbuf)
  41. + bufs_alloc_size;
  42. }
  43. extern lzma_ret
  44. lzma_outq_init(lzma_outq *outq, const lzma_allocator *allocator,
  45. uint64_t buf_size_max, uint32_t threads)
  46. {
  47. uint64_t bufs_alloc_size;
  48. uint32_t bufs_count;
  49. // Set bufs_count and bufs_alloc_size.
  50. return_if_error(get_options(&bufs_alloc_size, &bufs_count,
  51. buf_size_max, threads));
  52. // Allocate memory if needed.
  53. if (outq->buf_size_max != buf_size_max
  54. || outq->bufs_allocated != bufs_count) {
  55. lzma_outq_end(outq, allocator);
  56. #if SIZE_MAX < UINT64_MAX
  57. if (bufs_alloc_size > SIZE_MAX)
  58. return LZMA_MEM_ERROR;
  59. #endif
  60. outq->bufs = lzma_alloc(bufs_count * sizeof(lzma_outbuf),
  61. allocator);
  62. outq->bufs_mem = lzma_alloc((size_t)(bufs_alloc_size),
  63. allocator);
  64. if (outq->bufs == NULL || outq->bufs_mem == NULL) {
  65. lzma_outq_end(outq, allocator);
  66. return LZMA_MEM_ERROR;
  67. }
  68. }
  69. // Initialize the rest of the main structure. Initialization of
  70. // outq->bufs[] is done when they are actually needed.
  71. outq->buf_size_max = (size_t)(buf_size_max);
  72. outq->bufs_allocated = bufs_count;
  73. outq->bufs_pos = 0;
  74. outq->bufs_used = 0;
  75. outq->read_pos = 0;
  76. return LZMA_OK;
  77. }
  78. extern void
  79. lzma_outq_end(lzma_outq *outq, const lzma_allocator *allocator)
  80. {
  81. lzma_free(outq->bufs, allocator);
  82. outq->bufs = NULL;
  83. lzma_free(outq->bufs_mem, allocator);
  84. outq->bufs_mem = NULL;
  85. return;
  86. }
  87. extern lzma_outbuf *
  88. lzma_outq_get_buf(lzma_outq *outq)
  89. {
  90. // Caller must have checked it with lzma_outq_has_buf().
  91. assert(outq->bufs_used < outq->bufs_allocated);
  92. // Initialize the new buffer.
  93. lzma_outbuf *buf = &outq->bufs[outq->bufs_pos];
  94. buf->buf = outq->bufs_mem + outq->bufs_pos * outq->buf_size_max;
  95. buf->size = 0;
  96. buf->finished = false;
  97. // Update the queue state.
  98. if (++outq->bufs_pos == outq->bufs_allocated)
  99. outq->bufs_pos = 0;
  100. ++outq->bufs_used;
  101. return buf;
  102. }
  103. extern bool
  104. lzma_outq_is_readable(const lzma_outq *outq)
  105. {
  106. uint32_t i = outq->bufs_pos - outq->bufs_used;
  107. if (outq->bufs_pos < outq->bufs_used)
  108. i += outq->bufs_allocated;
  109. return outq->bufs[i].finished;
  110. }
  111. extern lzma_ret
  112. lzma_outq_read(lzma_outq *restrict outq, uint8_t *restrict out,
  113. size_t *restrict out_pos, size_t out_size,
  114. lzma_vli *restrict unpadded_size,
  115. lzma_vli *restrict uncompressed_size)
  116. {
  117. // There must be at least one buffer from which to read.
  118. if (outq->bufs_used == 0)
  119. return LZMA_OK;
  120. // Get the buffer.
  121. uint32_t i = outq->bufs_pos - outq->bufs_used;
  122. if (outq->bufs_pos < outq->bufs_used)
  123. i += outq->bufs_allocated;
  124. lzma_outbuf *buf = &outq->bufs[i];
  125. // If it isn't finished yet, we cannot read from it.
  126. if (!buf->finished)
  127. return LZMA_OK;
  128. // Copy from the buffer to output.
  129. lzma_bufcpy(buf->buf, &outq->read_pos, buf->size,
  130. out, out_pos, out_size);
  131. // Return if we didn't get all the data from the buffer.
  132. if (outq->read_pos < buf->size)
  133. return LZMA_OK;
  134. // The buffer was finished. Tell the caller its size information.
  135. *unpadded_size = buf->unpadded_size;
  136. *uncompressed_size = buf->uncompressed_size;
  137. // Free this buffer for further use.
  138. --outq->bufs_used;
  139. outq->read_pos = 0;
  140. return LZMA_STREAM_END;
  141. }