/src/mupdf/thirdparty/extract/src/buffer.c
Line | Count | Source |
1 | | #include "extract/buffer.h" |
2 | | #include "extract/alloc.h" |
3 | | |
4 | | #include "outf.h" |
5 | | |
6 | | #include <assert.h> |
7 | | #include <errno.h> |
8 | | #include <stdio.h> |
9 | | #include <stdlib.h> |
10 | | #include <string.h> |
11 | | |
12 | | /* TODO: Check whether the whole complexity of the cache is actually justified. */ |
13 | | |
14 | | struct extract_buffer_t |
15 | | { |
16 | | /* First member must be extract_buffer_cache_t - required by inline |
17 | | implementations of extract_buffer_read() and extract_buffer_write(). */ |
18 | | extract_buffer_cache_t cache; |
19 | | extract_alloc_t *alloc; |
20 | | void *handle; |
21 | | extract_buffer_fn_read *fn_read; |
22 | | extract_buffer_fn_write *fn_write; |
23 | | extract_buffer_fn_cache *fn_cache; |
24 | | extract_buffer_fn_close *fn_close; |
25 | | size_t pos; /* Does not include bytes currently read/written to cache. */ |
26 | | }; |
27 | | |
28 | | |
29 | | extract_alloc_t *extract_buffer_alloc(extract_buffer_t* buffer) |
30 | 0 | { |
31 | 0 | return buffer->alloc; |
32 | 0 | } |
33 | | |
34 | | |
35 | | int extract_buffer_open(extract_alloc_t *alloc, |
36 | | void *handle, |
37 | | extract_buffer_fn_read *fn_read, |
38 | | extract_buffer_fn_write *fn_write, |
39 | | extract_buffer_fn_cache *fn_cache, |
40 | | extract_buffer_fn_close *fn_close, |
41 | | extract_buffer_t **o_buffer) |
42 | 0 | { |
43 | 0 | extract_buffer_t *buffer; |
44 | |
|
45 | 0 | if (extract_malloc(alloc, &buffer, sizeof(*buffer))) |
46 | 0 | return -1; |
47 | | |
48 | 0 | buffer->alloc = alloc; |
49 | 0 | buffer->handle = handle; |
50 | 0 | buffer->fn_read = fn_read; |
51 | 0 | buffer->fn_write = fn_write; |
52 | 0 | buffer->fn_cache = fn_cache; |
53 | 0 | buffer->fn_close = fn_close; |
54 | 0 | buffer->cache.cache = NULL; |
55 | 0 | buffer->cache.numbytes = 0; |
56 | 0 | buffer->cache.pos = 0; |
57 | 0 | buffer->pos = 0; |
58 | |
|
59 | 0 | *o_buffer = buffer; |
60 | |
|
61 | 0 | return 0; |
62 | 0 | } |
63 | | |
64 | | |
65 | | size_t extract_buffer_pos(extract_buffer_t *buffer) |
66 | 0 | { |
67 | 0 | size_t ret = buffer->pos; |
68 | |
|
69 | 0 | if (buffer->cache.cache) |
70 | 0 | ret += buffer->cache.pos; |
71 | |
|
72 | 0 | return ret; |
73 | 0 | } |
74 | | |
75 | | |
76 | | /* Send contents of cache to fn_write() using a loop to cope with short |
77 | | writes. Returns with *o_actual containing the number of bytes successfully |
78 | | sent, and buffer->cache.{cache,numbytes,pos} all set to zero. |
79 | | |
80 | | If we return zero but *actual is less than original buffer->cache.numbytes, |
81 | | then fn_write returned EOF. */ |
82 | | static int cache_flush(extract_buffer_t *buffer, size_t *o_actual) |
83 | 0 | { |
84 | 0 | int e = -1; |
85 | 0 | size_t p = 0; |
86 | |
|
87 | 0 | assert(buffer->cache.pos <= buffer->cache.numbytes); |
88 | |
|
89 | 0 | while (p != buffer->cache.pos) |
90 | 0 | { |
91 | 0 | size_t actual; |
92 | 0 | if (buffer->fn_write( |
93 | 0 | buffer->handle, |
94 | 0 | (char*) buffer->cache.cache + p, |
95 | 0 | buffer->cache.pos - p, |
96 | 0 | &actual |
97 | 0 | )) goto end; |
98 | 0 | buffer->pos += actual; |
99 | 0 | p += actual; |
100 | 0 | if (actual == 0) |
101 | 0 | { |
102 | | /* EOF while flushing cache. We set <pos> to the |
103 | | * number of bytes in data..+numbytes that we know |
104 | | * have been successfully handled by buffer->fn_write(). |
105 | | * This can be negative if we failed to flush |
106 | | * earlier data. */ |
107 | 0 | outf("*** buffer->fn_write() EOF\n"); |
108 | 0 | e = 0; |
109 | 0 | goto end; |
110 | 0 | } |
111 | 0 | } |
112 | 0 | outfx("cache flush, buffer->pos=%i p=buffer->cache.pos=%i\n", |
113 | 0 | buffer->pos, p); |
114 | 0 | buffer->cache.cache = NULL; |
115 | 0 | buffer->cache.numbytes = 0; |
116 | 0 | buffer->cache.pos = 0; |
117 | |
|
118 | 0 | e = 0; |
119 | 0 | end: |
120 | 0 | *o_actual = p; |
121 | |
|
122 | 0 | return e; |
123 | 0 | } |
124 | | |
125 | | int extract_buffer_close(extract_buffer_t **p_buffer) |
126 | 0 | { |
127 | 0 | extract_buffer_t *buffer = *p_buffer; |
128 | 0 | int e = -1; |
129 | |
|
130 | 0 | if (buffer == NULL) |
131 | 0 | return 0; |
132 | | |
133 | 0 | if (buffer->cache.cache && buffer->fn_write) |
134 | 0 | { |
135 | | /* Flush cache. */ |
136 | 0 | size_t cache_bytes = buffer->cache.pos; |
137 | 0 | size_t actual; |
138 | 0 | if (cache_flush(buffer, &actual)) goto end; |
139 | 0 | if (actual != cache_bytes) |
140 | 0 | { |
141 | 0 | e = 1; |
142 | 0 | goto end; |
143 | 0 | } |
144 | 0 | } |
145 | | |
146 | 0 | if (buffer->fn_close) |
147 | 0 | buffer->fn_close(buffer->handle); |
148 | |
|
149 | 0 | e = 0; |
150 | 0 | end: |
151 | 0 | extract_free(buffer->alloc, &buffer); |
152 | 0 | *p_buffer = NULL; |
153 | |
|
154 | 0 | return e; |
155 | 0 | } |
156 | | |
157 | | static int simple_cache(void *handle, void **o_cache, size_t *o_numbytes) |
158 | 0 | { |
159 | | /* Indicate EOF. */ |
160 | 0 | (void) handle; |
161 | 0 | *o_cache = NULL; |
162 | 0 | *o_numbytes = 0; |
163 | |
|
164 | 0 | return 0; |
165 | 0 | } |
166 | | |
167 | | int extract_buffer_open_simple(extract_alloc_t *alloc, |
168 | | const void *data, |
169 | | size_t numbytes, |
170 | | void *handle, |
171 | | extract_buffer_fn_close *fn_close, |
172 | | extract_buffer_t **o_buffer) |
173 | 0 | { |
174 | 0 | extract_buffer_t *buffer; |
175 | |
|
176 | 0 | if (extract_malloc(alloc, &buffer, sizeof(*buffer))) |
177 | 0 | return -1; |
178 | | |
179 | | /* We need cast away the const here. data[] will be written-to if caller |
180 | | uses us as a write buffer. */ |
181 | 0 | buffer->alloc = alloc; |
182 | 0 | buffer->cache.cache = (void*) data; |
183 | 0 | buffer->cache.numbytes = numbytes; |
184 | 0 | buffer->cache.pos = 0; |
185 | 0 | buffer->handle = handle; |
186 | 0 | buffer->fn_read = NULL; |
187 | 0 | buffer->fn_write = NULL; |
188 | 0 | buffer->fn_cache = simple_cache; |
189 | 0 | buffer->fn_close = fn_close; |
190 | 0 | *o_buffer = buffer; |
191 | |
|
192 | 0 | return 0; |
193 | 0 | } |
194 | | |
195 | | |
196 | | /* Implementation of extract_buffer_file*. */ |
197 | | |
198 | | static int file_read(void *handle, void *data, size_t numbytes, size_t *o_actual) |
199 | 0 | { |
200 | 0 | FILE *file = handle; |
201 | 0 | size_t n = fread(data, 1, numbytes, file); |
202 | |
|
203 | 0 | outfx("file=%p numbytes=%i => n=%zi", file, numbytes, n); |
204 | 0 | assert(o_actual); /* We are called by other extract_buffer fns, not by user code. */ |
205 | |
|
206 | 0 | *o_actual = n; |
207 | 0 | if (n == 0 && ferror(file)) |
208 | 0 | { |
209 | 0 | errno = EIO; |
210 | 0 | return -1; |
211 | 0 | } |
212 | | |
213 | 0 | return 0; |
214 | 0 | } |
215 | | |
216 | | static int file_write(void *handle, const void *data, size_t numbytes, size_t *o_actual) |
217 | 0 | { |
218 | 0 | FILE *file = handle; |
219 | 0 | size_t n = fwrite(data, 1 /*size*/, numbytes /*nmemb*/, file); |
220 | |
|
221 | 0 | outfx("file=%p numbytes=%i => n=%zi", file, numbytes, n); |
222 | 0 | assert(o_actual); /* We are called by other extract_buffer fns, not by user code. */ |
223 | |
|
224 | 0 | *o_actual = n; |
225 | 0 | if (n == 0 && ferror(file)) |
226 | 0 | { |
227 | 0 | errno = EIO; |
228 | 0 | return -1; |
229 | 0 | } |
230 | | |
231 | 0 | return 0; |
232 | 0 | } |
233 | | |
234 | | static void file_close(void *handle) |
235 | 0 | { |
236 | 0 | FILE *file = handle; |
237 | |
|
238 | 0 | if (file) |
239 | 0 | fclose(file); |
240 | 0 | } |
241 | | |
242 | | int extract_buffer_open_file(extract_alloc_t *alloc, const char *path, int writable, extract_buffer_t **o_buffer) |
243 | 0 | { |
244 | 0 | int e = -1; |
245 | 0 | FILE *file = fopen(path, (writable) ? "wb" : "rb"); |
246 | |
|
247 | 0 | if (!file) |
248 | 0 | { |
249 | 0 | outf("failed to open '%s': %s", path, strerror(errno)); |
250 | 0 | goto end; |
251 | 0 | } |
252 | | |
253 | 0 | if (extract_buffer_open(alloc, |
254 | 0 | file /*handle*/, |
255 | 0 | writable ? NULL : file_read, |
256 | 0 | writable ? file_write : NULL, |
257 | 0 | NULL /*fn_cache*/, |
258 | 0 | file_close, |
259 | 0 | o_buffer)) goto end; |
260 | | |
261 | 0 | e = 0; |
262 | 0 | end: |
263 | |
|
264 | 0 | if (e) |
265 | 0 | { |
266 | 0 | if (file) |
267 | 0 | fclose(file); |
268 | 0 | *o_buffer = NULL; |
269 | 0 | } |
270 | |
|
271 | 0 | return e; |
272 | 0 | } |
273 | | |
274 | | |
275 | | /* Support for read/write. */ |
276 | | |
277 | | /* Called by extract_buffer_read() if not enough space in buffer->cache. */ |
278 | | int extract_buffer_read_internal(extract_buffer_t *buffer, |
279 | | void *destination, |
280 | | size_t numbytes, |
281 | | size_t *o_actual) |
282 | 0 | { |
283 | 0 | int e = -1; |
284 | 0 | size_t pos = 0; /* Number of bytes read so far. */ |
285 | | |
286 | | /* In each iteration we either read from cache, or use buffer->fn_read() |
287 | | directly or repopulate the cache. */ |
288 | 0 | while (pos != numbytes) |
289 | 0 | { |
290 | 0 | size_t n = buffer->cache.numbytes - buffer->cache.pos; |
291 | 0 | if (n) |
292 | 0 | { |
293 | | /* There is data in cache. */ |
294 | 0 | if (n > numbytes - pos) n = numbytes - pos; |
295 | 0 | memcpy((char *)destination + pos, (char *)buffer->cache.cache + buffer->cache.pos, n); |
296 | 0 | pos += n; |
297 | 0 | buffer->cache.pos += n; |
298 | 0 | } |
299 | | /* No data in the cache - do we use fn_read or fn_cache ? */ |
300 | 0 | else if (buffer->fn_read && |
301 | 0 | (buffer->fn_cache == NULL || |
302 | 0 | (buffer->cache.numbytes && numbytes - pos > buffer->cache.numbytes / 2))) |
303 | 0 | { |
304 | | /* Either there is no cache, or this read is large |
305 | | * compared to previously-returned cache size, so |
306 | | * let's ignore buffer->fn_cache and use |
307 | | * buffer->fn_read() directly instead. */ |
308 | | /* Carry on looping in case of short read. */ |
309 | 0 | size_t actual; |
310 | 0 | outfx("using buffer->fn_read() directly for numbytes-pos=%i\n", numbytes-pos); |
311 | 0 | if (buffer->fn_read(buffer->handle, (char*) destination + pos, numbytes - pos, &actual)) |
312 | 0 | goto end; |
313 | 0 | if (actual == 0) |
314 | 0 | break; /* EOF. */ |
315 | 0 | pos += actual; |
316 | 0 | buffer->pos += actual; |
317 | 0 | } |
318 | 0 | else |
319 | 0 | { |
320 | | /* Repopulate cache. */ |
321 | 0 | outfx("using buffer->fn_cache() for buffer->cache.numbytes=%i\n", buffer->cache.numbytes); |
322 | 0 | if (buffer->fn_cache(buffer->handle, &buffer->cache.cache, &buffer->cache.numbytes)) |
323 | 0 | goto end; |
324 | 0 | buffer->pos += buffer->cache.pos; |
325 | 0 | buffer->cache.pos = 0; |
326 | 0 | if (buffer->cache.numbytes == 0) |
327 | 0 | break; /* EOF. */ |
328 | 0 | } |
329 | 0 | } |
330 | | |
331 | 0 | e = 0; |
332 | 0 | end: |
333 | |
|
334 | 0 | if (o_actual) |
335 | 0 | *o_actual = pos; |
336 | 0 | if (e == 0 && pos != numbytes) |
337 | 0 | return +1; /* EOF. */ |
338 | | |
339 | 0 | return e; |
340 | 0 | } |
341 | | |
342 | | |
343 | | int extract_buffer_write_internal(extract_buffer_t *buffer, |
344 | | const void *source, |
345 | | size_t numbytes, |
346 | | size_t *o_actual) |
347 | 0 | { |
348 | 0 | int e = -1; |
349 | 0 | size_t pos = 0; /* Number of bytes written so far. */ |
350 | |
|
351 | 0 | if (buffer->fn_write == NULL) |
352 | 0 | { |
353 | 0 | errno = EINVAL; |
354 | 0 | return -1; |
355 | 0 | } |
356 | | |
357 | | /* In each iteration we either write to cache, or use buffer->fn_write() |
358 | | directly or flush the cache. */ |
359 | 0 | while (pos != numbytes) |
360 | 0 | { |
361 | 0 | size_t n = buffer->cache.numbytes - buffer->cache.pos; |
362 | 0 | outfx("numbytes=%i pos=%i. buffer->cache.numbytes=%i buffer->cache.pos=%i\n", |
363 | 0 | numbytes, pos, buffer->cache.numbytes, buffer->cache.pos); |
364 | 0 | if (n) |
365 | 0 | { |
366 | | /* There is space in cache for writing. */ |
367 | 0 | if (n > numbytes - pos) |
368 | 0 | n = numbytes - pos; |
369 | 0 | outfx("writing to cache: numbytes=%i n=%i\n", numbytes, n); |
370 | 0 | memcpy((char*) buffer->cache.cache + buffer->cache.pos, (char*) source + pos, n); |
371 | 0 | pos += n; |
372 | 0 | buffer->cache.pos += n; |
373 | 0 | } |
374 | 0 | else |
375 | 0 | { |
376 | | /* No space left in cache. */ |
377 | 0 | outfx("cache empty. pos=%i. buffer->cache.numbytes=%i buffer->cache.pos=%i\n", |
378 | 0 | pos, buffer->cache.numbytes, buffer->cache.pos); |
379 | 0 | { |
380 | | /* Flush the cache. */ |
381 | 0 | size_t actual; |
382 | 0 | size_t b = buffer->cache.numbytes; |
383 | 0 | ptrdiff_t delta; |
384 | 0 | int ee = cache_flush(buffer, &actual); |
385 | 0 | assert(actual <= b); |
386 | 0 | delta = actual - b; |
387 | 0 | pos += delta; |
388 | 0 | buffer->pos += delta; |
389 | 0 | if (delta) |
390 | 0 | { |
391 | | /* We have only partially flushed the cache. This |
392 | | * is not recoverable. <pos> will be the number of |
393 | | * bytes in source..+numbytes that have been |
394 | | * successfully flushed, and could be negative |
395 | | * if we failed to flush earlier data. */ |
396 | 0 | outf("failed to flush. actual=%li delta=%li\n", (long) actual, (long) delta); |
397 | 0 | e = 0; |
398 | 0 | goto end; |
399 | 0 | } |
400 | 0 | if (ee) goto end; |
401 | 0 | } |
402 | | |
403 | 0 | if (buffer->fn_cache == NULL || |
404 | 0 | (buffer->cache.numbytes && numbytes - pos > buffer->cache.numbytes / 2)) |
405 | 0 | { |
406 | | /* Either there is no cache, or this write is large |
407 | | * compared to previously-returned cache size, so let's |
408 | | * ignore the cache and call buffer->fn_write() |
409 | | * directly instead. Carry on looping in case of short |
410 | | * write. */ |
411 | 0 | size_t actual; |
412 | 0 | if (buffer->fn_write(buffer->handle, (char*) source + pos, numbytes - pos, &actual)) |
413 | 0 | goto end; |
414 | 0 | if (actual == 0) |
415 | 0 | break; /* EOF. */ |
416 | 0 | outfx("direct write numbytes-pos=%i actual=%i buffer->pos=%i => %i\n", |
417 | 0 | numbytes-pos, actual, buffer->pos, buffer->pos + actual); |
418 | 0 | pos += actual; |
419 | 0 | buffer->pos += actual; |
420 | 0 | } |
421 | 0 | else |
422 | 0 | { |
423 | | /* Repopulate cache. */ |
424 | 0 | outfx("repopulating cache buffer->pos=%i", buffer->pos); |
425 | 0 | if (buffer->fn_cache(buffer->handle, &buffer->cache.cache, &buffer->cache.numbytes)) |
426 | 0 | goto end; |
427 | 0 | buffer->cache.pos = 0; |
428 | 0 | if (buffer->cache.numbytes == 0) |
429 | 0 | break; /* EOF. */ |
430 | 0 | } |
431 | 0 | } |
432 | 0 | } |
433 | | |
434 | 0 | e = 0; |
435 | 0 | end: |
436 | |
|
437 | 0 | if (o_actual) |
438 | 0 | *o_actual = pos; |
439 | 0 | if (e == 0 && pos != numbytes) |
440 | 0 | e = +1; /* EOF. */ |
441 | |
|
442 | 0 | return e; |
443 | 0 | } |
444 | | |
445 | | |
446 | | static int expanding_memory_buffer_write(void *handle, const void *source, size_t numbytes, size_t *o_actual) |
447 | 0 | { |
448 | | /* We realloc our memory region as required. For efficiency, we also use |
449 | | * any currently-unused region of our memory buffer as an extract_buffer |
450 | | * cache. So we can be called either to 'flush the cache' (in which case we |
451 | | * don't actually copy any data) or to accept data from somewhere else (in |
452 | | * which case we need to increase the size of our memory region. */ |
453 | 0 | extract_buffer_expanding_t *ebe = handle; |
454 | 0 | if ((char *)source >= ebe->data && (char *)source < ebe->data + ebe->alloc_size) |
455 | 0 | { |
456 | | /* Source is inside our memory region so we are being called by |
457 | | * extract_buffer_write_internal() to re-populate the cache. We don't |
458 | | * actually have to copy anything. */ |
459 | 0 | assert((size_t) ((char *)source - ebe->data) == ebe->data_size); |
460 | 0 | assert((size_t) ((char *)source - ebe->data + numbytes) <= ebe->alloc_size); |
461 | 0 | ebe->data_size += numbytes; |
462 | 0 | } |
463 | 0 | else |
464 | 0 | { |
465 | | /* Data is external, so copy into our buffer. We will have already been |
466 | | called to flush the cache. */ |
467 | 0 | if (extract_realloc2(ebe->buffer->alloc, &ebe->data, ebe->alloc_size, ebe->data_size + numbytes)) |
468 | 0 | return -1; |
469 | 0 | ebe->alloc_size = ebe->data_size + numbytes; |
470 | 0 | memcpy(ebe->data + ebe->data_size, source, numbytes); |
471 | 0 | ebe->data_size += numbytes; |
472 | 0 | } |
473 | 0 | *o_actual = numbytes; |
474 | |
|
475 | 0 | return 0; |
476 | 0 | } |
477 | | |
478 | | static int expanding_memory_buffer_cache(void *handle, void **o_cache, size_t *o_numbytes) |
479 | 0 | { |
480 | 0 | extract_buffer_expanding_t *ebe = handle; |
481 | 0 | size_t delta = 4096; |
482 | |
|
483 | 0 | if (extract_realloc2(ebe->buffer->alloc, &ebe->data, ebe->alloc_size, ebe->data_size + delta)) |
484 | 0 | return -1; |
485 | | |
486 | 0 | ebe->alloc_size = ebe->data_size + delta; |
487 | 0 | *o_cache = ebe->data + ebe->data_size; |
488 | 0 | *o_numbytes = delta; |
489 | |
|
490 | 0 | return 0; |
491 | 0 | } |
492 | | |
493 | | int extract_buffer_expanding_create(extract_alloc_t *alloc, extract_buffer_expanding_t *ebe) |
494 | 0 | { |
495 | 0 | ebe->data = NULL; |
496 | 0 | ebe->data_size = 0; |
497 | 0 | ebe->alloc_size = 0; |
498 | 0 | if (extract_buffer_open(alloc, |
499 | 0 | ebe, |
500 | 0 | NULL /*fn_read*/, |
501 | 0 | expanding_memory_buffer_write, |
502 | 0 | expanding_memory_buffer_cache, |
503 | 0 | NULL /*fn_close*/, |
504 | 0 | &ebe->buffer)) |
505 | 0 | return -1; |
506 | | |
507 | 0 | return 0; |
508 | 0 | } |