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