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