VirtualBox

source: vbox/trunk/src/libs/liblzma-5.4.1/common/outqueue.c

最後變更 在這個檔案是 98730,由 vboxsync 提交於 21 月 前

libs/liblzma-5.4.1: Export to OSE, bugref:10254

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 6.7 KB
 
1///////////////////////////////////////////////////////////////////////////////
2//
3/// \file outqueue.c
4/// \brief Output queue handling in multithreaded coding
5//
6// Author: Lasse Collin
7//
8// This file has been put into the public domain.
9// You can do whatever you want with this file.
10//
11///////////////////////////////////////////////////////////////////////////////
12
13#include "outqueue.h"
14
15
16/// Get the maximum number of buffers that may be allocated based
17/// on the number of threads. For now this is twice the number of threads.
18/// It's a compromise between RAM usage and keeping the worker threads busy
19/// when buffers finish out of order.
20#define GET_BUFS_LIMIT(threads) (2 * (threads))
21
22
23extern uint64_t
24lzma_outq_memusage(uint64_t buf_size_max, uint32_t threads)
25{
26 // This is to ease integer overflow checking: We may allocate up to
27 // GET_BUFS_LIMIT(LZMA_THREADS_MAX) buffers and we need some extra
28 // memory for other data structures too (that's the /2).
29 //
30 // lzma_outq_prealloc_buf() will still accept bigger buffers than this.
31 const uint64_t limit
32 = UINT64_MAX / GET_BUFS_LIMIT(LZMA_THREADS_MAX) / 2;
33
34 if (threads > LZMA_THREADS_MAX || buf_size_max > limit)
35 return UINT64_MAX;
36
37 return GET_BUFS_LIMIT(threads)
38 * lzma_outq_outbuf_memusage(buf_size_max);
39}
40
41
42static void
43move_head_to_cache(lzma_outq *outq, const lzma_allocator *allocator)
44{
45 assert(outq->head != NULL);
46 assert(outq->tail != NULL);
47 assert(outq->bufs_in_use > 0);
48
49 lzma_outbuf *buf = outq->head;
50 outq->head = buf->next;
51 if (outq->head == NULL)
52 outq->tail = NULL;
53
54 if (outq->cache != NULL && outq->cache->allocated != buf->allocated)
55 lzma_outq_clear_cache(outq, allocator);
56
57 buf->next = outq->cache;
58 outq->cache = buf;
59
60 --outq->bufs_in_use;
61 outq->mem_in_use -= lzma_outq_outbuf_memusage(buf->allocated);
62
63 return;
64}
65
66
67static void
68free_one_cached_buffer(lzma_outq *outq, const lzma_allocator *allocator)
69{
70 assert(outq->cache != NULL);
71
72 lzma_outbuf *buf = outq->cache;
73 outq->cache = buf->next;
74
75 --outq->bufs_allocated;
76 outq->mem_allocated -= lzma_outq_outbuf_memusage(buf->allocated);
77
78 lzma_free(buf, allocator);
79 return;
80}
81
82
83extern void
84lzma_outq_clear_cache(lzma_outq *outq, const lzma_allocator *allocator)
85{
86 while (outq->cache != NULL)
87 free_one_cached_buffer(outq, allocator);
88
89 return;
90}
91
92
93extern void
94lzma_outq_clear_cache2(lzma_outq *outq, const lzma_allocator *allocator,
95 size_t keep_size)
96{
97 if (outq->cache == NULL)
98 return;
99
100 // Free all but one.
101 while (outq->cache->next != NULL)
102 free_one_cached_buffer(outq, allocator);
103
104 // Free the last one only if its size doesn't equal to keep_size.
105 if (outq->cache->allocated != keep_size)
106 free_one_cached_buffer(outq, allocator);
107
108 return;
109}
110
111
112extern lzma_ret
113lzma_outq_init(lzma_outq *outq, const lzma_allocator *allocator,
114 uint32_t threads)
115{
116 if (threads > LZMA_THREADS_MAX)
117 return LZMA_OPTIONS_ERROR;
118
119 const uint32_t bufs_limit = GET_BUFS_LIMIT(threads);
120
121 // Clear head/tail.
122 while (outq->head != NULL)
123 move_head_to_cache(outq, allocator);
124
125 // If new buf_limit is lower than the old one, we may need to free
126 // a few cached buffers.
127 while (bufs_limit < outq->bufs_allocated)
128 free_one_cached_buffer(outq, allocator);
129
130 outq->bufs_limit = bufs_limit;
131 outq->read_pos = 0;
132
133 return LZMA_OK;
134}
135
136
137extern void
138lzma_outq_end(lzma_outq *outq, const lzma_allocator *allocator)
139{
140 while (outq->head != NULL)
141 move_head_to_cache(outq, allocator);
142
143 lzma_outq_clear_cache(outq, allocator);
144 return;
145}
146
147
148extern lzma_ret
149lzma_outq_prealloc_buf(lzma_outq *outq, const lzma_allocator *allocator,
150 size_t size)
151{
152 // Caller must have checked it with lzma_outq_has_buf().
153 assert(outq->bufs_in_use < outq->bufs_limit);
154
155 // If there already is appropriately-sized buffer in the cache,
156 // we need to do nothing.
157 if (outq->cache != NULL && outq->cache->allocated == size)
158 return LZMA_OK;
159
160 if (size > SIZE_MAX - sizeof(lzma_outbuf))
161 return LZMA_MEM_ERROR;
162
163 const size_t alloc_size = lzma_outq_outbuf_memusage(size);
164
165 // The cache may have buffers but their size is wrong.
166 lzma_outq_clear_cache(outq, allocator);
167
168 outq->cache = lzma_alloc(alloc_size, allocator);
169 if (outq->cache == NULL)
170 return LZMA_MEM_ERROR;
171
172 outq->cache->next = NULL;
173 outq->cache->allocated = size;
174
175 ++outq->bufs_allocated;
176 outq->mem_allocated += alloc_size;
177
178 return LZMA_OK;
179}
180
181
182extern lzma_outbuf *
183lzma_outq_get_buf(lzma_outq *outq, void *worker)
184{
185 // Caller must have used lzma_outq_prealloc_buf() to ensure these.
186 assert(outq->bufs_in_use < outq->bufs_limit);
187 assert(outq->bufs_in_use < outq->bufs_allocated);
188 assert(outq->cache != NULL);
189
190 lzma_outbuf *buf = outq->cache;
191 outq->cache = buf->next;
192 buf->next = NULL;
193
194 if (outq->tail != NULL) {
195 assert(outq->head != NULL);
196 outq->tail->next = buf;
197 } else {
198 assert(outq->head == NULL);
199 outq->head = buf;
200 }
201
202 outq->tail = buf;
203
204 buf->worker = worker;
205 buf->finished = false;
206 buf->finish_ret = LZMA_STREAM_END;
207 buf->pos = 0;
208 buf->decoder_in_pos = 0;
209
210 buf->unpadded_size = 0;
211 buf->uncompressed_size = 0;
212
213 ++outq->bufs_in_use;
214 outq->mem_in_use += lzma_outq_outbuf_memusage(buf->allocated);
215
216 return buf;
217}
218
219
220extern bool
221lzma_outq_is_readable(const lzma_outq *outq)
222{
223 if (outq->head == NULL)
224 return false;
225
226 return outq->read_pos < outq->head->pos || outq->head->finished;
227}
228
229
230extern lzma_ret
231lzma_outq_read(lzma_outq *restrict outq,
232 const lzma_allocator *restrict allocator,
233 uint8_t *restrict out, size_t *restrict out_pos,
234 size_t out_size,
235 lzma_vli *restrict unpadded_size,
236 lzma_vli *restrict uncompressed_size)
237{
238 // There must be at least one buffer from which to read.
239 if (outq->bufs_in_use == 0)
240 return LZMA_OK;
241
242 // Get the buffer.
243 lzma_outbuf *buf = outq->head;
244
245 // Copy from the buffer to output.
246 //
247 // FIXME? In threaded decoder it may be bad to do this copy while
248 // the mutex is being held.
249 lzma_bufcpy(buf->buf, &outq->read_pos, buf->pos,
250 out, out_pos, out_size);
251
252 // Return if we didn't get all the data from the buffer.
253 if (!buf->finished || outq->read_pos < buf->pos)
254 return LZMA_OK;
255
256 // The buffer was finished. Tell the caller its size information.
257 if (unpadded_size != NULL)
258 *unpadded_size = buf->unpadded_size;
259
260 if (uncompressed_size != NULL)
261 *uncompressed_size = buf->uncompressed_size;
262
263 // Remember the return value.
264 const lzma_ret finish_ret = buf->finish_ret;
265
266 // Free this buffer for further use.
267 move_head_to_cache(outq, allocator);
268 outq->read_pos = 0;
269
270 return finish_ret;
271}
272
273
274extern void
275lzma_outq_enable_partial_output(lzma_outq *outq,
276 void (*enable_partial_output)(void *worker))
277{
278 if (outq->head != NULL && !outq->head->finished
279 && outq->head->worker != NULL) {
280 enable_partial_output(outq->head->worker);
281
282 // Set it to NULL since calling it twice is pointless.
283 outq->head->worker = NULL;
284 }
285
286 return;
287}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette