Fix version.sh compatiblity with Solaris
[xz/debian.git] / src / liblzma / common / outqueue.c
blobeb018eb42b26922793de7287fc83941adb46a2da
1 // SPDX-License-Identifier: 0BSD
3 ///////////////////////////////////////////////////////////////////////////////
4 //
5 /// \file outqueue.c
6 /// \brief Output queue handling in multithreaded coding
7 //
8 // Author: Lasse Collin
9 //
10 ///////////////////////////////////////////////////////////////////////////////
12 #include "outqueue.h"
15 /// Get the maximum number of buffers that may be allocated based
16 /// on the number of threads. For now this is twice the number of threads.
17 /// It's a compromise between RAM usage and keeping the worker threads busy
18 /// when buffers finish out of order.
19 #define GET_BUFS_LIMIT(threads) (2 * (threads))
22 extern uint64_t
23 lzma_outq_memusage(uint64_t buf_size_max, uint32_t threads)
25 // This is to ease integer overflow checking: We may allocate up to
26 // GET_BUFS_LIMIT(LZMA_THREADS_MAX) buffers and we need some extra
27 // memory for other data structures too (that's the /2).
29 // lzma_outq_prealloc_buf() will still accept bigger buffers than this.
30 const uint64_t limit
31 = UINT64_MAX / GET_BUFS_LIMIT(LZMA_THREADS_MAX) / 2;
33 if (threads > LZMA_THREADS_MAX || buf_size_max > limit)
34 return UINT64_MAX;
36 return GET_BUFS_LIMIT(threads)
37 * lzma_outq_outbuf_memusage(buf_size_max);
41 static void
42 move_head_to_cache(lzma_outq *outq, const lzma_allocator *allocator)
44 assert(outq->head != NULL);
45 assert(outq->tail != NULL);
46 assert(outq->bufs_in_use > 0);
48 lzma_outbuf *buf = outq->head;
49 outq->head = buf->next;
50 if (outq->head == NULL)
51 outq->tail = NULL;
53 if (outq->cache != NULL && outq->cache->allocated != buf->allocated)
54 lzma_outq_clear_cache(outq, allocator);
56 buf->next = outq->cache;
57 outq->cache = buf;
59 --outq->bufs_in_use;
60 outq->mem_in_use -= lzma_outq_outbuf_memusage(buf->allocated);
62 return;
66 static void
67 free_one_cached_buffer(lzma_outq *outq, const lzma_allocator *allocator)
69 assert(outq->cache != NULL);
71 lzma_outbuf *buf = outq->cache;
72 outq->cache = buf->next;
74 --outq->bufs_allocated;
75 outq->mem_allocated -= lzma_outq_outbuf_memusage(buf->allocated);
77 lzma_free(buf, allocator);
78 return;
82 extern void
83 lzma_outq_clear_cache(lzma_outq *outq, const lzma_allocator *allocator)
85 while (outq->cache != NULL)
86 free_one_cached_buffer(outq, allocator);
88 return;
92 extern void
93 lzma_outq_clear_cache2(lzma_outq *outq, const lzma_allocator *allocator,
94 size_t keep_size)
96 if (outq->cache == NULL)
97 return;
99 // Free all but one.
100 while (outq->cache->next != NULL)
101 free_one_cached_buffer(outq, allocator);
103 // Free the last one only if its size doesn't equal to keep_size.
104 if (outq->cache->allocated != keep_size)
105 free_one_cached_buffer(outq, allocator);
107 return;
111 extern lzma_ret
112 lzma_outq_init(lzma_outq *outq, const lzma_allocator *allocator,
113 uint32_t threads)
115 if (threads > LZMA_THREADS_MAX)
116 return LZMA_OPTIONS_ERROR;
118 const uint32_t bufs_limit = GET_BUFS_LIMIT(threads);
120 // Clear head/tail.
121 while (outq->head != NULL)
122 move_head_to_cache(outq, allocator);
124 // If new buf_limit is lower than the old one, we may need to free
125 // a few cached buffers.
126 while (bufs_limit < outq->bufs_allocated)
127 free_one_cached_buffer(outq, allocator);
129 outq->bufs_limit = bufs_limit;
130 outq->read_pos = 0;
132 return LZMA_OK;
136 extern void
137 lzma_outq_end(lzma_outq *outq, const lzma_allocator *allocator)
139 while (outq->head != NULL)
140 move_head_to_cache(outq, allocator);
142 lzma_outq_clear_cache(outq, allocator);
143 return;
147 extern lzma_ret
148 lzma_outq_prealloc_buf(lzma_outq *outq, const lzma_allocator *allocator,
149 size_t size)
151 // Caller must have checked it with lzma_outq_has_buf().
152 assert(outq->bufs_in_use < outq->bufs_limit);
154 // If there already is appropriately-sized buffer in the cache,
155 // we need to do nothing.
156 if (outq->cache != NULL && outq->cache->allocated == size)
157 return LZMA_OK;
159 if (size > SIZE_MAX - sizeof(lzma_outbuf))
160 return LZMA_MEM_ERROR;
162 const size_t alloc_size = lzma_outq_outbuf_memusage(size);
164 // The cache may have buffers but their size is wrong.
165 lzma_outq_clear_cache(outq, allocator);
167 outq->cache = lzma_alloc(alloc_size, allocator);
168 if (outq->cache == NULL)
169 return LZMA_MEM_ERROR;
171 outq->cache->next = NULL;
172 outq->cache->allocated = size;
174 ++outq->bufs_allocated;
175 outq->mem_allocated += alloc_size;
177 return LZMA_OK;
181 extern lzma_outbuf *
182 lzma_outq_get_buf(lzma_outq *outq, void *worker)
184 // Caller must have used lzma_outq_prealloc_buf() to ensure these.
185 assert(outq->bufs_in_use < outq->bufs_limit);
186 assert(outq->bufs_in_use < outq->bufs_allocated);
187 assert(outq->cache != NULL);
189 lzma_outbuf *buf = outq->cache;
190 outq->cache = buf->next;
191 buf->next = NULL;
193 if (outq->tail != NULL) {
194 assert(outq->head != NULL);
195 outq->tail->next = buf;
196 } else {
197 assert(outq->head == NULL);
198 outq->head = buf;
201 outq->tail = buf;
203 buf->worker = worker;
204 buf->finished = false;
205 buf->finish_ret = LZMA_STREAM_END;
206 buf->pos = 0;
207 buf->decoder_in_pos = 0;
209 buf->unpadded_size = 0;
210 buf->uncompressed_size = 0;
212 ++outq->bufs_in_use;
213 outq->mem_in_use += lzma_outq_outbuf_memusage(buf->allocated);
215 return buf;
219 extern bool
220 lzma_outq_is_readable(const lzma_outq *outq)
222 if (outq->head == NULL)
223 return false;
225 return outq->read_pos < outq->head->pos || outq->head->finished;
229 extern lzma_ret
230 lzma_outq_read(lzma_outq *restrict outq,
231 const lzma_allocator *restrict allocator,
232 uint8_t *restrict out, size_t *restrict out_pos,
233 size_t out_size,
234 lzma_vli *restrict unpadded_size,
235 lzma_vli *restrict uncompressed_size)
237 // There must be at least one buffer from which to read.
238 if (outq->bufs_in_use == 0)
239 return LZMA_OK;
241 // Get the buffer.
242 lzma_outbuf *buf = outq->head;
244 // Copy from the buffer to output.
246 // FIXME? In threaded decoder it may be bad to do this copy while
247 // the mutex is being held.
248 lzma_bufcpy(buf->buf, &outq->read_pos, buf->pos,
249 out, out_pos, out_size);
251 // Return if we didn't get all the data from the buffer.
252 if (!buf->finished || outq->read_pos < buf->pos)
253 return LZMA_OK;
255 // The buffer was finished. Tell the caller its size information.
256 if (unpadded_size != NULL)
257 *unpadded_size = buf->unpadded_size;
259 if (uncompressed_size != NULL)
260 *uncompressed_size = buf->uncompressed_size;
262 // Remember the return value.
263 const lzma_ret finish_ret = buf->finish_ret;
265 // Free this buffer for further use.
266 move_head_to_cache(outq, allocator);
267 outq->read_pos = 0;
269 return finish_ret;
273 extern void
274 lzma_outq_enable_partial_output(lzma_outq *outq,
275 void (*enable_partial_output)(void *worker))
277 if (outq->head != NULL && !outq->head->finished
278 && outq->head->worker != NULL) {
279 enable_partial_output(outq->head->worker);
281 // Set it to NULL since calling it twice is pointless.
282 outq->head->worker = NULL;
285 return;