Path: blob/master/Utilities/cmliblzma/liblzma/common/outqueue.c
3153 views
// SPDX-License-Identifier: 0BSD12///////////////////////////////////////////////////////////////////////////////3//4/// \file outqueue.c5/// \brief Output queue handling in multithreaded coding6//7// Author: Lasse Collin8//9///////////////////////////////////////////////////////////////////////////////1011#include "outqueue.h"121314/// Get the maximum number of buffers that may be allocated based15/// on the number of threads. For now this is twice the number of threads.16/// It's a compromise between RAM usage and keeping the worker threads busy17/// when buffers finish out of order.18#define GET_BUFS_LIMIT(threads) (2 * (threads))192021extern uint64_t22lzma_outq_memusage(uint64_t buf_size_max, uint32_t threads)23{24// This is to ease integer overflow checking: We may allocate up to25// GET_BUFS_LIMIT(LZMA_THREADS_MAX) buffers and we need some extra26// memory for other data structures too (that's the /2).27//28// lzma_outq_prealloc_buf() will still accept bigger buffers than this.29const uint64_t limit30= UINT64_MAX / GET_BUFS_LIMIT(LZMA_THREADS_MAX) / 2;3132if (threads > LZMA_THREADS_MAX || buf_size_max > limit)33return UINT64_MAX;3435return GET_BUFS_LIMIT(threads)36* lzma_outq_outbuf_memusage(buf_size_max);37}383940static void41move_head_to_cache(lzma_outq *outq, const lzma_allocator *allocator)42{43assert(outq->head != NULL);44assert(outq->tail != NULL);45assert(outq->bufs_in_use > 0);4647lzma_outbuf *buf = outq->head;48outq->head = buf->next;49if (outq->head == NULL)50outq->tail = NULL;5152if (outq->cache != NULL && outq->cache->allocated != buf->allocated)53lzma_outq_clear_cache(outq, allocator);5455buf->next = outq->cache;56outq->cache = buf;5758--outq->bufs_in_use;59outq->mem_in_use -= lzma_outq_outbuf_memusage(buf->allocated);6061return;62}636465static void66free_one_cached_buffer(lzma_outq *outq, const lzma_allocator *allocator)67{68assert(outq->cache != NULL);6970lzma_outbuf *buf = outq->cache;71outq->cache = buf->next;7273--outq->bufs_allocated;74outq->mem_allocated -= lzma_outq_outbuf_memusage(buf->allocated);7576lzma_free(buf, allocator);77return;78}798081extern void82lzma_outq_clear_cache(lzma_outq *outq, const lzma_allocator *allocator)83{84while (outq->cache != NULL)85free_one_cached_buffer(outq, allocator);8687return;88}899091extern void92lzma_outq_clear_cache2(lzma_outq *outq, const lzma_allocator *allocator,93size_t keep_size)94{95if (outq->cache == NULL)96return;9798// Free all but one.99while (outq->cache->next != NULL)100free_one_cached_buffer(outq, allocator);101102// Free the last one only if its size doesn't equal to keep_size.103if (outq->cache->allocated != keep_size)104free_one_cached_buffer(outq, allocator);105106return;107}108109110extern lzma_ret111lzma_outq_init(lzma_outq *outq, const lzma_allocator *allocator,112uint32_t threads)113{114if (threads > LZMA_THREADS_MAX)115return LZMA_OPTIONS_ERROR;116117const uint32_t bufs_limit = GET_BUFS_LIMIT(threads);118119// Clear head/tail.120while (outq->head != NULL)121move_head_to_cache(outq, allocator);122123// If new buf_limit is lower than the old one, we may need to free124// a few cached buffers.125while (bufs_limit < outq->bufs_allocated)126free_one_cached_buffer(outq, allocator);127128outq->bufs_limit = bufs_limit;129outq->read_pos = 0;130131return LZMA_OK;132}133134135extern void136lzma_outq_end(lzma_outq *outq, const lzma_allocator *allocator)137{138while (outq->head != NULL)139move_head_to_cache(outq, allocator);140141lzma_outq_clear_cache(outq, allocator);142return;143}144145146extern lzma_ret147lzma_outq_prealloc_buf(lzma_outq *outq, const lzma_allocator *allocator,148size_t size)149{150// Caller must have checked it with lzma_outq_has_buf().151assert(outq->bufs_in_use < outq->bufs_limit);152153// If there already is appropriately-sized buffer in the cache,154// we need to do nothing.155if (outq->cache != NULL && outq->cache->allocated == size)156return LZMA_OK;157158if (size > SIZE_MAX - sizeof(lzma_outbuf))159return LZMA_MEM_ERROR;160161const size_t alloc_size = lzma_outq_outbuf_memusage(size);162163// The cache may have buffers but their size is wrong.164lzma_outq_clear_cache(outq, allocator);165166outq->cache = lzma_alloc(alloc_size, allocator);167if (outq->cache == NULL)168return LZMA_MEM_ERROR;169170outq->cache->next = NULL;171outq->cache->allocated = size;172173++outq->bufs_allocated;174outq->mem_allocated += alloc_size;175176return LZMA_OK;177}178179180extern lzma_outbuf *181lzma_outq_get_buf(lzma_outq *outq, void *worker)182{183// Caller must have used lzma_outq_prealloc_buf() to ensure these.184assert(outq->bufs_in_use < outq->bufs_limit);185assert(outq->bufs_in_use < outq->bufs_allocated);186assert(outq->cache != NULL);187188lzma_outbuf *buf = outq->cache;189outq->cache = buf->next;190buf->next = NULL;191192if (outq->tail != NULL) {193assert(outq->head != NULL);194outq->tail->next = buf;195} else {196assert(outq->head == NULL);197outq->head = buf;198}199200outq->tail = buf;201202buf->worker = worker;203buf->finished = false;204buf->finish_ret = LZMA_STREAM_END;205buf->pos = 0;206buf->decoder_in_pos = 0;207208buf->unpadded_size = 0;209buf->uncompressed_size = 0;210211++outq->bufs_in_use;212outq->mem_in_use += lzma_outq_outbuf_memusage(buf->allocated);213214return buf;215}216217218extern bool219lzma_outq_is_readable(const lzma_outq *outq)220{221if (outq->head == NULL)222return false;223224return outq->read_pos < outq->head->pos || outq->head->finished;225}226227228extern lzma_ret229lzma_outq_read(lzma_outq *restrict outq,230const lzma_allocator *restrict allocator,231uint8_t *restrict out, size_t *restrict out_pos,232size_t out_size,233lzma_vli *restrict unpadded_size,234lzma_vli *restrict uncompressed_size)235{236// There must be at least one buffer from which to read.237if (outq->bufs_in_use == 0)238return LZMA_OK;239240// Get the buffer.241lzma_outbuf *buf = outq->head;242243// Copy from the buffer to output.244//245// FIXME? In threaded decoder it may be bad to do this copy while246// the mutex is being held.247lzma_bufcpy(buf->buf, &outq->read_pos, buf->pos,248out, out_pos, out_size);249250// Return if we didn't get all the data from the buffer.251if (!buf->finished || outq->read_pos < buf->pos)252return LZMA_OK;253254// The buffer was finished. Tell the caller its size information.255if (unpadded_size != NULL)256*unpadded_size = buf->unpadded_size;257258if (uncompressed_size != NULL)259*uncompressed_size = buf->uncompressed_size;260261// Remember the return value.262const lzma_ret finish_ret = buf->finish_ret;263264// Free this buffer for further use.265move_head_to_cache(outq, allocator);266outq->read_pos = 0;267268return finish_ret;269}270271272extern void273lzma_outq_enable_partial_output(lzma_outq *outq,274void (*enable_partial_output)(void *worker))275{276if (outq->head != NULL && !outq->head->finished277&& outq->head->worker != NULL) {278enable_partial_output(outq->head->worker);279280// Set it to NULL since calling it twice is pointless.281outq->head->worker = NULL;282}283284return;285}286287288