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