Home | History | Annotate | Line # | Download | only in common
      1 // SPDX-License-Identifier: 0BSD
      2 
      3 ///////////////////////////////////////////////////////////////////////////////
      4 //
      5 /// \file       outqueue.h
      6 /// \brief      Output queue handling in multithreaded coding
      7 //
      8 //  Author:     Lasse Collin
      9 //
     10 ///////////////////////////////////////////////////////////////////////////////
     11 
     12 #ifndef LZMA_OUTQUEUE_H
     13 #define LZMA_OUTQUEUE_H
     14 
     15 #include "common.h"
     16 
     17 
     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.
     23 	lzma_outbuf *next;
     24 
     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.
     28 	void *worker;
     29 
     30 	/// Amount of memory allocated for buf[].
     31 	/// The worker thread must not modify this.
     32 	size_t allocated;
     33 
     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
     36 	///
     37 	/// \note       This is read by another thread and thus access
     38 	///             to this variable needs a mutex.
     39 	size_t pos;
     40 
     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.
     45 	///
     46 	/// \note       This is read by another thread and thus access
     47 	///             to this variable needs a mutex.
     48 	size_t decoder_in_pos;
     49 
     50 	/// True when no more data will be written into this buffer.
     51 	///
     52 	/// \note       This is read by another thread and thus access
     53 	///             to this variable needs a mutex.
     54 	bool finished;
     55 
     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.
     61 	lzma_ret finish_ret;
     62 
     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;
     67 
     68 	/// Buffer of "allocated" bytes
     69 	uint8_t buf[];
     70 };
     71 
     72 
     73 typedef struct {
     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.
     77 	lzma_outbuf *head;
     78 	lzma_outbuf *tail;
     79 
     80 	/// Number of bytes read from head->buf[] in lzma_outq_read()
     81 	size_t read_pos;
     82 
     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.
     87 	lzma_outbuf *cache;
     88 
     89 	/// Total amount of memory allocated for buffers
     90 	uint64_t mem_allocated;
     91 
     92 	/// Amount of memory used by the buffers that are in use in
     93 	/// the head...tail linked list.
     94 	uint64_t mem_in_use;
     95 
     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.
     98 	uint32_t bufs_in_use;
     99 
    100 	/// Number of buffers allocated (in use + cached)
    101 	uint32_t bufs_allocated;
    102 
    103 	/// Maximum allowed number of allocated buffers
    104 	uint32_t bufs_limit;
    105 } lzma_outq;
    106 
    107 
    108 /**
    109  * \brief       Calculate the memory usage of an output queue
    110  *
    111  * \return      Approximate memory usage in bytes or UINT64_MAX on error.
    112  */
    113 extern uint64_t lzma_outq_memusage(uint64_t buf_size_max, uint32_t threads);
    114 
    115 
    116 /// \brief      Initialize an output queue
    117 ///
    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
    130 ///                             of threads.
    131 ///
    132 /// \return     - LZMA_OK
    133 ///             - LZMA_MEM_ERROR
    134 ///
    135 extern lzma_ret lzma_outq_init(lzma_outq *outq,
    136 		const lzma_allocator *allocator, uint32_t threads);
    137 
    138 
    139 /// \brief      Free the memory associated with the output queue
    140 extern void lzma_outq_end(lzma_outq *outq, const lzma_allocator *allocator);
    141 
    142 
    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);
    146 
    147 
    148 /// \brief      Like lzma_outq_clear_cache() but might keep one buffer
    149 ///
    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,
    155 		size_t keep_size);
    156 
    157 
    158 /// \brief      Preallocate a new buffer into cache
    159 ///
    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.
    165 ///
    166 /// \return     LZMA_OK on success, LZMA_MEM_ERROR if allocation fails
    167 ///
    168 extern lzma_ret lzma_outq_prealloc_buf(
    169 		lzma_outq *outq, const lzma_allocator *allocator, size_t size);
    170 
    171 
    172 /// \brief      Get a new buffer
    173 ///
    174 /// lzma_outq_prealloc_buf() must be used to ensure that there is a buffer
    175 /// available before calling lzma_outq_get_buf().
    176 ///
    177 extern lzma_outbuf *lzma_outq_get_buf(lzma_outq *outq, void *worker);
    178 
    179 
    180 /// \brief      Test if there is data ready to be read
    181 ///
    182 /// Call to this function must be protected with the same mutex that
    183 /// is used to protect lzma_outbuf.finished.
    184 ///
    185 extern bool lzma_outq_is_readable(const lzma_outq *outq);
    186 
    187 
    188 /// \brief      Read finished data
    189 ///
    190 /// \param      outq            Pointer to an output queue
    191 /// \param      allocator       lzma_allocator for custom allocator functions
    192 /// \param      out             Beginning of the output buffer
    193 /// \param      out_pos         The next byte will be written to
    194 ///                             out[*out_pos].
    195 /// \param      out_size        Size of the out buffer; the first byte into
    196 ///                             which no data is written to is out[out_size].
    197 /// \param      unpadded_size   Unpadded Size from the Block encoder
    198 /// \param      uncompressed_size Uncompressed Size from the Block encoder
    199 ///
    200 /// \return     - LZMA: All OK. Either no data was available or the buffer
    201 ///               being read didn't become empty yet.
    202 ///             - LZMA_STREAM_END: The buffer being read was finished.
    203 ///               *unpadded_size and *uncompressed_size were set if they
    204 ///               were not NULL.
    205 ///
    206 /// \note       This reads lzma_outbuf.finished and .pos variables and thus
    207 ///             calls to this function need to be protected with a mutex.
    208 ///
    209 extern lzma_ret lzma_outq_read(lzma_outq *restrict outq,
    210 		const lzma_allocator *restrict allocator,
    211 		uint8_t *restrict out, size_t *restrict out_pos,
    212 		size_t out_size, lzma_vli *restrict unpadded_size,
    213 		lzma_vli *restrict uncompressed_size);
    214 
    215 
    216 /// \brief      Enable partial output from a worker thread
    217 ///
    218 /// If the buffer at the head of the output queue isn't finished,
    219 /// this will call enable_partial_output on the worker associated with
    220 /// that output buffer.
    221 ///
    222 /// \note       This reads a lzma_outbuf.finished variable and thus
    223 ///             calls to this function need to be protected with a mutex.
    224 ///
    225 extern void lzma_outq_enable_partial_output(lzma_outq *outq,
    226 		void (*enable_partial_output)(void *worker));
    227 
    228 
    229 /// \brief      Test if there is at least one buffer free
    230 ///
    231 /// This must be used before getting a new buffer with lzma_outq_get_buf().
    232 ///
    233 static inline bool
    234 lzma_outq_has_buf(const lzma_outq *outq)
    235 {
    236 	return outq->bufs_in_use < outq->bufs_limit;
    237 }
    238 
    239 
    240 /// \brief      Test if the queue is completely empty
    241 static inline bool
    242 lzma_outq_is_empty(const lzma_outq *outq)
    243 {
    244 	return outq->bufs_in_use == 0;
    245 }
    246 
    247 
    248 /// \brief      Get the amount of memory needed for a single lzma_outbuf
    249 ///
    250 /// \note       Caller must check that the argument is significantly less
    251 ///             than SIZE_MAX to avoid an integer overflow!
    252 static inline uint64_t
    253 lzma_outq_outbuf_memusage(size_t buf_size)
    254 {
    255 	assert(buf_size <= SIZE_MAX - sizeof(lzma_outbuf));
    256 	return sizeof(lzma_outbuf) + buf_size;
    257 }
    258 
    259 #endif
    260