2011-04-11 22:03:30 +03:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
/// \file outqueue.c
|
|
|
|
/// \brief Output queue handling in multithreaded coding
|
|
|
|
//
|
|
|
|
// Author: Lasse Collin
|
|
|
|
//
|
|
|
|
// This file has been put into the public domain.
|
|
|
|
// You can do whatever you want with this file.
|
|
|
|
//
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#include "outqueue.h"
|
|
|
|
|
|
|
|
|
2021-01-09 21:14:36 +02:00
|
|
|
/// Get the maximum number of buffers that may be allocated based
|
|
|
|
/// on the number of threads. For now this is twice the number of threads.
|
|
|
|
/// It's a compromise between RAM usage and keeping the worker threads busy
|
|
|
|
/// when buffers finish out of order.
|
|
|
|
#define GET_BUFS_LIMIT(threads) (2 * (threads))
|
2011-04-11 22:03:30 +03:00
|
|
|
|
|
|
|
|
2021-01-09 21:14:36 +02:00
|
|
|
extern uint64_t
|
|
|
|
lzma_outq_memusage(uint64_t buf_size_max, uint32_t threads)
|
2011-04-11 22:03:30 +03:00
|
|
|
{
|
2021-01-09 21:14:36 +02:00
|
|
|
// This is to ease integer overflow checking: We may allocate up to
|
|
|
|
// GET_BUFS_LIMIT(LZMA_THREADS_MAX) buffers and we need some extra
|
|
|
|
// memory for other data structures too (that's the /2).
|
2011-04-11 22:03:30 +03:00
|
|
|
//
|
2021-01-09 21:14:36 +02:00
|
|
|
// lzma_outq_prealloc_buf() will still accept bigger buffers than this.
|
|
|
|
const uint64_t limit
|
|
|
|
= UINT64_MAX / GET_BUFS_LIMIT(LZMA_THREADS_MAX) / 2;
|
2011-04-11 22:03:30 +03:00
|
|
|
|
2021-01-09 21:14:36 +02:00
|
|
|
if (threads > LZMA_THREADS_MAX || buf_size_max > limit)
|
|
|
|
return UINT64_MAX;
|
|
|
|
|
2022-03-06 16:41:19 +02:00
|
|
|
return GET_BUFS_LIMIT(threads)
|
|
|
|
* lzma_outq_outbuf_memusage(buf_size_max);
|
2011-04-11 22:03:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-01-09 21:14:36 +02:00
|
|
|
static void
|
|
|
|
move_head_to_cache(lzma_outq *outq, const lzma_allocator *allocator)
|
2011-04-11 22:03:30 +03:00
|
|
|
{
|
2021-01-09 21:14:36 +02:00
|
|
|
assert(outq->head != NULL);
|
|
|
|
assert(outq->tail != NULL);
|
|
|
|
assert(outq->bufs_in_use > 0);
|
|
|
|
|
|
|
|
lzma_outbuf *buf = outq->head;
|
|
|
|
outq->head = buf->next;
|
|
|
|
if (outq->head == NULL)
|
|
|
|
outq->tail = NULL;
|
|
|
|
|
|
|
|
if (outq->cache != NULL && outq->cache->allocated != buf->allocated)
|
|
|
|
lzma_outq_clear_cache(outq, allocator);
|
|
|
|
|
|
|
|
buf->next = outq->cache;
|
|
|
|
outq->cache = buf;
|
|
|
|
|
2022-03-06 16:41:19 +02:00
|
|
|
--outq->bufs_in_use;
|
|
|
|
outq->mem_in_use -= lzma_outq_outbuf_memusage(buf->allocated);
|
|
|
|
|
2021-01-09 21:14:36 +02:00
|
|
|
return;
|
|
|
|
}
|
2011-04-11 22:03:30 +03:00
|
|
|
|
|
|
|
|
2021-01-09 21:14:36 +02:00
|
|
|
static void
|
|
|
|
free_one_cached_buffer(lzma_outq *outq, const lzma_allocator *allocator)
|
|
|
|
{
|
|
|
|
assert(outq->cache != NULL);
|
|
|
|
|
|
|
|
lzma_outbuf *buf = outq->cache;
|
|
|
|
outq->cache = buf->next;
|
|
|
|
|
|
|
|
--outq->bufs_allocated;
|
2022-03-06 16:41:19 +02:00
|
|
|
outq->mem_allocated -= lzma_outq_outbuf_memusage(buf->allocated);
|
2021-01-09 21:14:36 +02:00
|
|
|
|
|
|
|
lzma_free(buf, allocator);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
extern void
|
|
|
|
lzma_outq_clear_cache(lzma_outq *outq, const lzma_allocator *allocator)
|
|
|
|
{
|
|
|
|
while (outq->cache != NULL)
|
|
|
|
free_one_cached_buffer(outq, allocator);
|
|
|
|
|
|
|
|
return;
|
2011-04-11 22:03:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-03-06 16:41:19 +02:00
|
|
|
extern void
|
|
|
|
lzma_outq_clear_cache2(lzma_outq *outq, const lzma_allocator *allocator,
|
|
|
|
size_t keep_size)
|
|
|
|
{
|
|
|
|
if (outq->cache == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Free all but one.
|
|
|
|
while (outq->cache->next != NULL)
|
|
|
|
free_one_cached_buffer(outq, allocator);
|
|
|
|
|
|
|
|
// Free the last one only if its size doesn't equal to keep_size.
|
|
|
|
if (outq->cache->allocated != keep_size)
|
|
|
|
free_one_cached_buffer(outq, allocator);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-04-11 22:03:30 +03:00
|
|
|
extern lzma_ret
|
2012-07-17 18:19:59 +03:00
|
|
|
lzma_outq_init(lzma_outq *outq, const lzma_allocator *allocator,
|
2021-01-09 21:14:36 +02:00
|
|
|
uint32_t threads)
|
2011-04-11 22:03:30 +03:00
|
|
|
{
|
2021-01-09 21:14:36 +02:00
|
|
|
if (threads > LZMA_THREADS_MAX)
|
|
|
|
return LZMA_OPTIONS_ERROR;
|
|
|
|
|
|
|
|
const uint32_t bufs_limit = GET_BUFS_LIMIT(threads);
|
|
|
|
|
|
|
|
// Clear head/tail.
|
|
|
|
while (outq->head != NULL)
|
|
|
|
move_head_to_cache(outq, allocator);
|
|
|
|
|
|
|
|
// If new buf_limit is lower than the old one, we may need to free
|
|
|
|
// a few cached buffers.
|
|
|
|
while (bufs_limit < outq->bufs_allocated)
|
|
|
|
free_one_cached_buffer(outq, allocator);
|
2011-04-11 22:03:30 +03:00
|
|
|
|
2021-01-09 21:14:36 +02:00
|
|
|
outq->bufs_limit = bufs_limit;
|
2011-04-11 22:03:30 +03:00
|
|
|
outq->read_pos = 0;
|
|
|
|
|
|
|
|
return LZMA_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
extern void
|
2012-07-17 18:19:59 +03:00
|
|
|
lzma_outq_end(lzma_outq *outq, const lzma_allocator *allocator)
|
2011-04-11 22:03:30 +03:00
|
|
|
{
|
2021-01-09 21:14:36 +02:00
|
|
|
while (outq->head != NULL)
|
|
|
|
move_head_to_cache(outq, allocator);
|
2011-10-23 17:08:14 +03:00
|
|
|
|
2021-01-09 21:14:36 +02:00
|
|
|
lzma_outq_clear_cache(outq, allocator);
|
2011-04-11 22:03:30 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-01-09 21:14:36 +02:00
|
|
|
extern lzma_ret
|
|
|
|
lzma_outq_prealloc_buf(lzma_outq *outq, const lzma_allocator *allocator,
|
|
|
|
size_t size)
|
2011-04-11 22:03:30 +03:00
|
|
|
{
|
|
|
|
// Caller must have checked it with lzma_outq_has_buf().
|
2021-01-09 21:14:36 +02:00
|
|
|
assert(outq->bufs_in_use < outq->bufs_limit);
|
|
|
|
|
|
|
|
// If there already is appropriately-sized buffer in the cache,
|
|
|
|
// we need to do nothing.
|
|
|
|
if (outq->cache != NULL && outq->cache->allocated == size)
|
|
|
|
return LZMA_OK;
|
|
|
|
|
|
|
|
if (size > SIZE_MAX - sizeof(lzma_outbuf))
|
|
|
|
return LZMA_MEM_ERROR;
|
|
|
|
|
2022-03-06 16:41:19 +02:00
|
|
|
const size_t alloc_size = lzma_outq_outbuf_memusage(size);
|
|
|
|
|
2021-01-09 21:14:36 +02:00
|
|
|
// The cache may have buffers but their size is wrong.
|
|
|
|
lzma_outq_clear_cache(outq, allocator);
|
|
|
|
|
2022-03-06 16:41:19 +02:00
|
|
|
outq->cache = lzma_alloc(alloc_size, allocator);
|
2021-01-09 21:14:36 +02:00
|
|
|
if (outq->cache == NULL)
|
|
|
|
return LZMA_MEM_ERROR;
|
|
|
|
|
|
|
|
outq->cache->next = NULL;
|
|
|
|
outq->cache->allocated = size;
|
2011-04-11 22:03:30 +03:00
|
|
|
|
2021-01-09 21:14:36 +02:00
|
|
|
++outq->bufs_allocated;
|
2022-03-06 16:41:19 +02:00
|
|
|
outq->mem_allocated += alloc_size;
|
2021-01-09 21:14:36 +02:00
|
|
|
|
|
|
|
return LZMA_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
extern lzma_outbuf *
|
|
|
|
lzma_outq_get_buf(lzma_outq *outq, void *worker)
|
|
|
|
{
|
|
|
|
// Caller must have used lzma_outq_prealloc_buf() to ensure these.
|
|
|
|
assert(outq->bufs_in_use < outq->bufs_limit);
|
|
|
|
assert(outq->bufs_in_use < outq->bufs_allocated);
|
|
|
|
assert(outq->cache != NULL);
|
|
|
|
|
|
|
|
lzma_outbuf *buf = outq->cache;
|
|
|
|
outq->cache = buf->next;
|
|
|
|
buf->next = NULL;
|
|
|
|
|
|
|
|
if (outq->tail != NULL) {
|
|
|
|
assert(outq->head != NULL);
|
|
|
|
outq->tail->next = buf;
|
|
|
|
} else {
|
|
|
|
assert(outq->head == NULL);
|
|
|
|
outq->head = buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
outq->tail = buf;
|
|
|
|
|
|
|
|
buf->worker = worker;
|
2011-04-11 22:03:30 +03:00
|
|
|
buf->finished = false;
|
2022-03-06 16:41:19 +02:00
|
|
|
buf->finish_ret = LZMA_STREAM_END;
|
2021-01-09 21:14:36 +02:00
|
|
|
buf->pos = 0;
|
2022-03-06 16:41:19 +02:00
|
|
|
buf->decoder_in_pos = 0;
|
2011-04-11 22:03:30 +03:00
|
|
|
|
2021-01-09 21:14:36 +02:00
|
|
|
buf->unpadded_size = 0;
|
|
|
|
buf->uncompressed_size = 0;
|
2011-04-11 22:03:30 +03:00
|
|
|
|
2021-01-09 21:14:36 +02:00
|
|
|
++outq->bufs_in_use;
|
2022-03-06 16:41:19 +02:00
|
|
|
outq->mem_in_use += lzma_outq_outbuf_memusage(buf->allocated);
|
2011-04-11 22:03:30 +03:00
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
extern bool
|
|
|
|
lzma_outq_is_readable(const lzma_outq *outq)
|
|
|
|
{
|
2021-01-09 21:14:36 +02:00
|
|
|
if (outq->head == NULL)
|
|
|
|
return false;
|
2011-04-11 22:03:30 +03:00
|
|
|
|
2021-01-09 21:14:36 +02:00
|
|
|
return outq->read_pos < outq->head->pos || outq->head->finished;
|
2011-04-11 22:03:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
extern lzma_ret
|
2021-01-09 21:14:36 +02:00
|
|
|
lzma_outq_read(lzma_outq *restrict outq,
|
|
|
|
const lzma_allocator *restrict allocator,
|
|
|
|
uint8_t *restrict out, size_t *restrict out_pos,
|
|
|
|
size_t out_size,
|
2011-04-11 22:03:30 +03:00
|
|
|
lzma_vli *restrict unpadded_size,
|
|
|
|
lzma_vli *restrict uncompressed_size)
|
|
|
|
{
|
|
|
|
// There must be at least one buffer from which to read.
|
2021-01-09 21:14:36 +02:00
|
|
|
if (outq->bufs_in_use == 0)
|
2011-04-11 22:03:30 +03:00
|
|
|
return LZMA_OK;
|
|
|
|
|
|
|
|
// Get the buffer.
|
2021-01-09 21:14:36 +02:00
|
|
|
lzma_outbuf *buf = outq->head;
|
2011-04-11 22:03:30 +03:00
|
|
|
|
|
|
|
// Copy from the buffer to output.
|
2021-01-09 21:14:36 +02:00
|
|
|
//
|
|
|
|
// FIXME? In threaded decoder it may be bad to do this copy while
|
|
|
|
// the mutex is being held.
|
|
|
|
lzma_bufcpy(buf->buf, &outq->read_pos, buf->pos,
|
2011-04-11 22:03:30 +03:00
|
|
|
out, out_pos, out_size);
|
|
|
|
|
|
|
|
// Return if we didn't get all the data from the buffer.
|
2021-01-09 21:14:36 +02:00
|
|
|
if (!buf->finished || outq->read_pos < buf->pos)
|
2011-04-11 22:03:30 +03:00
|
|
|
return LZMA_OK;
|
|
|
|
|
|
|
|
// The buffer was finished. Tell the caller its size information.
|
2021-01-09 21:14:36 +02:00
|
|
|
if (unpadded_size != NULL)
|
|
|
|
*unpadded_size = buf->unpadded_size;
|
|
|
|
|
|
|
|
if (uncompressed_size != NULL)
|
|
|
|
*uncompressed_size = buf->uncompressed_size;
|
2011-04-11 22:03:30 +03:00
|
|
|
|
2022-03-06 16:41:19 +02:00
|
|
|
// Remember the return value.
|
|
|
|
const lzma_ret finish_ret = buf->finish_ret;
|
|
|
|
|
2011-04-11 22:03:30 +03:00
|
|
|
// Free this buffer for further use.
|
2021-01-09 21:14:36 +02:00
|
|
|
move_head_to_cache(outq, allocator);
|
2011-04-11 22:03:30 +03:00
|
|
|
outq->read_pos = 0;
|
|
|
|
|
2022-03-06 16:41:19 +02:00
|
|
|
return finish_ret;
|
2011-04-11 22:03:30 +03:00
|
|
|
}
|
2021-01-09 21:14:36 +02:00
|
|
|
|
|
|
|
|
|
|
|
extern void
|
|
|
|
lzma_outq_enable_partial_output(lzma_outq *outq,
|
|
|
|
void (*enable_partial_output)(void *worker))
|
|
|
|
{
|
|
|
|
if (outq->head != NULL && !outq->head->finished
|
|
|
|
&& outq->head->worker != NULL) {
|
|
|
|
enable_partial_output(outq->head->worker);
|
|
|
|
|
|
|
|
// Set it to NULL since calling it twice is pointless.
|
|
|
|
outq->head->worker = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|