1 // SPDX-License-Identifier: 0BSD
3 ///////////////////////////////////////////////////////////////////////////////
6 /// \brief Output queue handling in multithreaded coding
8 // Author: Lasse Collin
10 ///////////////////////////////////////////////////////////////////////////////
12 #ifndef LZMA_OUTQUEUE_H
13 #define LZMA_OUTQUEUE_H
18 /// Output buffer for a single thread
19 typedef struct lzma_outbuf_s lzma_outbuf
;
20 struct lzma_outbuf_s
{
21 /// Pointer to the next buffer. This is used for the cached buffers.
22 /// The worker thread must not modify this.
25 /// This initialized by lzma_outq_get_buf() and
26 /// is used by lzma_outq_enable_partial_output().
27 /// The worker thread must not modify this.
30 /// Amount of memory allocated for buf[].
31 /// The worker thread must not modify this.
34 /// Writing position in the worker thread or, in other words, the
35 /// amount of finished data written to buf[] which can be copied out
37 /// \note This is read by another thread and thus access
38 /// to this variable needs a mutex.
41 /// Decompression: Position in the input buffer in the worker thread
42 /// that matches the output "pos" above. This is used to detect if
43 /// more output might be possible from the worker thread: if it has
44 /// consumed all its input, then more output isn't possible.
46 /// \note This is read by another thread and thus access
47 /// to this variable needs a mutex.
48 size_t decoder_in_pos
;
50 /// True when no more data will be written into this buffer.
52 /// \note This is read by another thread and thus access
53 /// to this variable needs a mutex.
56 /// Return value for lzma_outq_read() when the last byte from
57 /// a finished buffer has been read. Defaults to LZMA_STREAM_END.
58 /// This must *not* be LZMA_OK. The idea is to allow a decoder to
59 /// pass an error code to the main thread, setting the code here
60 /// together with finished = true.
63 /// Additional size information. lzma_outq_read() may read these
64 /// when "finished" is true.
65 lzma_vli unpadded_size
;
66 lzma_vli uncompressed_size
;
68 /// Buffer of "allocated" bytes
74 /// Linked list of buffers in use. The next output byte will be
75 /// read from the head and buffers for the next thread will be
76 /// appended to the tail. tail->next is always NULL.
80 /// Number of bytes read from head->buf[] in lzma_outq_read()
83 /// Linked list of allocated buffers that aren't currently used.
84 /// This way buffers of similar size can be reused and don't
85 /// need to be reallocated every time. For simplicity, all
86 /// cached buffers in the list have the same allocated size.
89 /// Total amount of memory allocated for buffers
90 uint64_t mem_allocated
;
92 /// Amount of memory used by the buffers that are in use in
93 /// the head...tail linked list.
96 /// Number of buffers in use in the head...tail list. If and only if
97 /// this is zero, the pointers head and tail above are NULL.
100 /// Number of buffers allocated (in use + cached)
101 uint32_t bufs_allocated
;
103 /// Maximum allowed number of allocated buffers
109 * \brief Calculate the memory usage of an output queue
111 * \return Approximate memory usage in bytes or UINT64_MAX on error.
113 extern uint64_t lzma_outq_memusage(uint64_t buf_size_max
, uint32_t threads
);
116 /// \brief Initialize an output queue
118 /// \param outq Pointer to an output queue. Before calling
119 /// this function the first time, *outq should
120 /// have been zeroed with memzero() so that this
121 /// function knows that there are no previous
122 /// allocations to free.
123 /// \param allocator Pointer to allocator or NULL
124 /// \param threads Number of buffers that may be in use
125 /// concurrently. Note that more than this number
126 /// of buffers may actually get allocated to
127 /// improve performance when buffers finish
128 /// out of order. The actual maximum number of
129 /// allocated buffers is derived from the number
132 /// \return - LZMA_OK
135 extern lzma_ret
lzma_outq_init(lzma_outq
*outq
,
136 const lzma_allocator
*allocator
, uint32_t threads
);
139 /// \brief Free the memory associated with the output queue
140 extern void lzma_outq_end(lzma_outq
*outq
, const lzma_allocator
*allocator
);
143 /// \brief Free all cached buffers that consume memory but aren't in use
144 extern void lzma_outq_clear_cache(
145 lzma_outq
*outq
, const lzma_allocator
*allocator
);
148 /// \brief Like lzma_outq_clear_cache() but might keep one buffer
150 /// One buffer is not freed if its size is equal to keep_size.
151 /// This is useful if the caller knows that it will soon need a buffer of
152 /// keep_size bytes. This way it won't be freed and immediately reallocated.
153 extern void lzma_outq_clear_cache2(
154 lzma_outq
*outq
, const lzma_allocator
*allocator
,
158 /// \brief Preallocate a new buffer into cache
160 /// Splitting the buffer allocation into a separate function makes it
161 /// possible to ensure that way lzma_outq_get_buf() cannot fail.
162 /// If the preallocated buffer isn't actually used (for example, some
163 /// other error occurs), the caller has to do nothing as the buffer will
164 /// be used later or cleared from the cache when not needed.
166 /// \return LZMA_OK on success, LZMA_MEM_ERROR if allocation fails
168 extern lzma_ret
lzma_outq_prealloc_buf(
169 lzma_outq
*outq
, const lzma_allocator
*allocator
, size_t size
);
172 /// \brief Get a new buffer
174 /// lzma_outq_prealloc_buf() must be used to ensure that there is a buffer
175 /// available before calling lzma_outq_get_buf().
177 extern lzma_outbuf
*lzma_outq_get_buf(lzma_outq
*outq
, void *worker
);
180 /// \brief Test if there is data ready to be read
182 /// Call to this function must be protected with the same mutex that
183 /// is used to protect lzma_outbuf.finished.
185 extern bool lzma_outq_is_readable(const lzma_outq
*outq
);
188 /// \brief Read finished data
190 /// \param outq Pointer to an output queue
191 /// \param out Beginning of the output buffer
192 /// \param out_pos The next byte will be written to
194 /// \param out_size Size of the out buffer; the first byte into
195 /// which no data is written to is out[out_size].
196 /// \param unpadded_size Unpadded Size from the Block encoder
197 /// \param uncompressed_size Uncompressed Size from the Block encoder
199 /// \return - LZMA: All OK. Either no data was available or the buffer
200 /// being read didn't become empty yet.
201 /// - LZMA_STREAM_END: The buffer being read was finished.
202 /// *unpadded_size and *uncompressed_size were set if they
205 /// \note This reads lzma_outbuf.finished and .pos variables and thus
206 /// calls to this function need to be protected with a mutex.
208 extern lzma_ret
lzma_outq_read(lzma_outq
*restrict outq
,
209 const lzma_allocator
*restrict allocator
,
210 uint8_t *restrict out
, size_t *restrict out_pos
,
211 size_t out_size
, lzma_vli
*restrict unpadded_size
,
212 lzma_vli
*restrict uncompressed_size
);
215 /// \brief Enable partial output from a worker thread
217 /// If the buffer at the head of the output queue isn't finished,
218 /// this will call enable_partial_output on the worker associated with
219 /// that output buffer.
221 /// \note This reads a lzma_outbuf.finished variable and thus
222 /// calls to this function need to be protected with a mutex.
224 extern void lzma_outq_enable_partial_output(lzma_outq
*outq
,
225 void (*enable_partial_output
)(void *worker
));
228 /// \brief Test if there is at least one buffer free
230 /// This must be used before getting a new buffer with lzma_outq_get_buf().
233 lzma_outq_has_buf(const lzma_outq
*outq
)
235 return outq
->bufs_in_use
< outq
->bufs_limit
;
239 /// \brief Test if the queue is completely empty
241 lzma_outq_is_empty(const lzma_outq
*outq
)
243 return outq
->bufs_in_use
== 0;
247 /// \brief Get the amount of memory needed for a single lzma_outbuf
249 /// \note Caller must check that the argument is significantly less
250 /// than SIZE_MAX to avoid an integer overflow!
251 static inline uint64_t
252 lzma_outq_outbuf_memusage(size_t buf_size
)
254 assert(buf_size
<= SIZE_MAX
- sizeof(lzma_outbuf
));
255 return sizeof(lzma_outbuf
) + buf_size
;