#include "outqueue.h"
#define GET_BUFS_LIMIT(threads) (2 * (threads))
extern uint64_t
lzma_outq_memusage(uint64_t buf_size_max, uint32_t threads)
{
const uint64_t limit
= UINT64_MAX / GET_BUFS_LIMIT(LZMA_THREADS_MAX) / 2;
if (threads > LZMA_THREADS_MAX || buf_size_max > limit)
return UINT64_MAX;
return GET_BUFS_LIMIT(threads)
* lzma_outq_outbuf_memusage(buf_size_max);
}
static void
move_head_to_cache(lzma_outq *outq, const lzma_allocator *allocator)
{
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;
--outq->bufs_in_use;
outq->mem_in_use -= lzma_outq_outbuf_memusage(buf->allocated);
return;
}
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;
outq->mem_allocated -= lzma_outq_outbuf_memusage(buf->allocated);
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;
}
extern void
lzma_outq_clear_cache2(lzma_outq *outq, const lzma_allocator *allocator,
size_t keep_size)
{
if (outq->cache == NULL)
return;
while (outq->cache->next != NULL)
free_one_cached_buffer(outq, allocator);
if (outq->cache->allocated != keep_size)
free_one_cached_buffer(outq, allocator);
return;
}
extern lzma_ret
lzma_outq_init(lzma_outq *outq, const lzma_allocator *allocator,
uint32_t threads)
{
if (threads > LZMA_THREADS_MAX)
return LZMA_OPTIONS_ERROR;
const uint32_t bufs_limit = GET_BUFS_LIMIT(threads);
while (outq->head != NULL)
move_head_to_cache(outq, allocator);
while (bufs_limit < outq->bufs_allocated)
free_one_cached_buffer(outq, allocator);
outq->bufs_limit = bufs_limit;
outq->read_pos = 0;
return LZMA_OK;
}
extern void
lzma_outq_end(lzma_outq *outq, const lzma_allocator *allocator)
{
while (outq->head != NULL)
move_head_to_cache(outq, allocator);
lzma_outq_clear_cache(outq, allocator);
return;
}
extern lzma_ret
lzma_outq_prealloc_buf(lzma_outq *outq, const lzma_allocator *allocator,
size_t size)
{
assert(outq->bufs_in_use < outq->bufs_limit);
if (outq->cache != NULL && outq->cache->allocated == size)
return LZMA_OK;
if (size > SIZE_MAX - sizeof(lzma_outbuf))
return LZMA_MEM_ERROR;
const size_t alloc_size = lzma_outq_outbuf_memusage(size);
lzma_outq_clear_cache(outq, allocator);
outq->cache = lzma_alloc(alloc_size, allocator);
if (outq->cache == NULL)
return LZMA_MEM_ERROR;
outq->cache->next = NULL;
outq->cache->allocated = size;
++outq->bufs_allocated;
outq->mem_allocated += alloc_size;
return LZMA_OK;
}
extern lzma_outbuf *
lzma_outq_get_buf(lzma_outq *outq, void *worker)
{
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;
buf->finished = false;
buf->finish_ret = LZMA_STREAM_END;
buf->pos = 0;
buf->decoder_in_pos = 0;
buf->unpadded_size = 0;
buf->uncompressed_size = 0;
++outq->bufs_in_use;
outq->mem_in_use += lzma_outq_outbuf_memusage(buf->allocated);
return buf;
}
extern bool
lzma_outq_is_readable(const lzma_outq *outq)
{
if (outq->head == NULL)
return false;
return outq->read_pos < outq->head->pos || outq->head->finished;
}
extern lzma_ret
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,
lzma_vli *restrict unpadded_size,
lzma_vli *restrict uncompressed_size)
{
if (outq->bufs_in_use == 0)
return LZMA_OK;
lzma_outbuf *buf = outq->head;
lzma_bufcpy(buf->buf, &outq->read_pos, buf->pos,
out, out_pos, out_size);
if (!buf->finished || outq->read_pos < buf->pos)
return LZMA_OK;
if (unpadded_size != NULL)
*unpadded_size = buf->unpadded_size;
if (uncompressed_size != NULL)
*uncompressed_size = buf->uncompressed_size;
const lzma_ret finish_ret = buf->finish_ret;
move_head_to_cache(outq, allocator);
outq->read_pos = 0;
return finish_ret;
}
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);
outq->head->worker = NULL;
}
return;
}