source: mainline/uspace/lib/riff/src/chunk.c@ 20667af

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 20667af was 20667af, checked in by Jiri Svoboda <jiri@…>, 5 years ago

Remember current position in libriff and optimize sequential reading

TPF files currently contain a lot of chunks and each chunk means
a ftell/fseek. libc's ftell/fseek flush the I/O buffer, leading
to a lot of unnecessary IPC. This all leads to reading typeface to
take way too long.

We remember the current file position
in a RIFF reader and avoid ftell and also avoid fseek at the end
of a chunk, if possible (if we need to advance just a little bit,
we read instead of seek).

  • Property mode set to 100644
File size: 11.3 KB
Line 
1/*
2 * Copyright (c) 2015 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libriff
30 * @{
31 */
32/**
33 * @file RIFF chunk.
34 */
35
36#include <assert.h>
37#include <byteorder.h>
38#include <errno.h>
39#include <macros.h>
40#include <riff/chunk.h>
41#include <stdbool.h>
42#include <stdlib.h>
43
44/** Open RIFF file for writing
45 *
46 * @param fname File name
47 * @param rrw Place to store pointer to RIFF writer
48 *
49 * @return EOK on success, ENOMEM if out of memory, EIO if failed to open
50 * file.
51 */
52errno_t riff_wopen(const char *fname, riffw_t **rrw)
53{
54 riffw_t *rw;
55
56 rw = calloc(1, sizeof(riffw_t));
57 if (rw == NULL)
58 return ENOMEM;
59
60 rw->f = fopen(fname, "wb");
61 if (rw->f == NULL) {
62 free(rw);
63 return EIO;
64 }
65
66 *rrw = rw;
67 return EOK;
68}
69
70/** Close RIFF for writing.
71 *
72 * @param rw RIFF writer
73 * @return EOK on success. On write error EIO is returned and RIFF writer
74 * is destroyed anyway.
75 */
76errno_t riff_wclose(riffw_t *rw)
77{
78 int rv;
79
80 rv = fclose(rw->f);
81 free(rw);
82
83 return (rv == 0) ? EOK : EIO;
84}
85
86/** Write uint32_t value into RIFF file
87 *
88 * @param rw RIFF writer
89 * @param v Value
90 * @return EOK on success, EIO on error.
91 */
92errno_t riff_write_uint32(riffw_t *rw, uint32_t v)
93{
94 uint32_t vle;
95
96 vle = host2uint32_t_le(v);
97 if (fwrite(&vle, 1, sizeof(vle), rw->f) < sizeof(vle))
98 return EIO;
99
100 return EOK;
101}
102
103/** Begin writing chunk.
104 *
105 * @param rw RIFF writer
106 * @param ckid Chunk ID
107 * @param wchunk Pointer to chunk structure to fill in
108 *
109 * @return EOK on success, EIO on write error
110 */
111errno_t riff_wchunk_start(riffw_t *rw, riff_ckid_t ckid, riff_wchunk_t *wchunk)
112{
113 long pos;
114 errno_t rc;
115
116 pos = ftell(rw->f);
117 if (pos < 0)
118 return EIO;
119
120 wchunk->ckstart = pos + 2 * sizeof(uint32_t);
121
122 rc = riff_write_uint32(rw, ckid);
123 if (rc != EOK) {
124 assert(rc == EIO);
125 return EIO;
126 }
127
128 rc = riff_write_uint32(rw, 0);
129 if (rc != EOK) {
130 assert(rc == EIO);
131 return EIO;
132 }
133
134 return EOK;
135}
136
137/** Finish writing chunk.
138 *
139 * @param rw RIFF writer
140 * @param wchunk Pointer to chunk structure
141 *
142 * @return EOK on success, EIO error.
143 */
144errno_t riff_wchunk_end(riffw_t *rw, riff_wchunk_t *wchunk)
145{
146 long pos;
147 long cksize;
148 errno_t rc;
149
150 pos = ftell(rw->f);
151 if (pos < 0)
152 return EIO;
153
154 cksize = pos - wchunk->ckstart;
155 if (pos % 2 != 0)
156 ++pos;
157
158 if (fseek(rw->f, wchunk->ckstart - 4, SEEK_SET) < 0)
159 return EIO;
160
161 rc = riff_write_uint32(rw, cksize);
162 if (rc != EOK) {
163 assert(rc == EIO);
164 return EIO;
165 }
166
167 if (fseek(rw->f, pos, SEEK_SET) < 0)
168 return EIO;
169
170 return EOK;
171}
172
173/** Write data into RIFF file.
174 *
175 * @param rw RIFF writer
176 * @param data Pointer to data
177 * @param bytes Number of bytes to write
178 *
179 * @return EOK on success, EIO on error.
180 */
181errno_t riff_write(riffw_t *rw, void *data, size_t bytes)
182{
183 size_t nw;
184
185 nw = fwrite(data, 1, bytes, rw->f);
186 if (nw != bytes)
187 return EIO;
188
189 return EOK;
190}
191
192/** Open RIFF file for reading.
193 *
194 * @param fname File name
195 * @param riffck Place to store root (RIFF) chunk
196 * @param rrr Place to store pointer to RIFF reader
197 *
198 * @return EOK on success, ENOMEM if out of memory, EIO if failed to open
199 * file..
200 */
201errno_t riff_ropen(const char *fname, riff_rchunk_t *riffck, riffr_t **rrr)
202{
203 riffr_t *rr;
204 riff_rchunk_t fchunk;
205 long fsize;
206 errno_t rc;
207 int rv;
208
209 rr = calloc(1, sizeof(riffr_t));
210 if (rr == NULL) {
211 rc = ENOMEM;
212 goto error;
213 }
214
215 rr->pos = 0;
216
217 rr->f = fopen(fname, "rb");
218 if (rr->f == NULL) {
219 rc = EIO;
220 goto error;
221 }
222
223 rv = fseek(rr->f, 0, SEEK_END);
224 if (rv < 0) {
225 rc = EIO;
226 goto error;
227 }
228
229 fsize = ftell(rr->f);
230 if (fsize < 0) {
231 rc = EIO;
232 goto error;
233 }
234
235 rv = fseek(rr->f, 0, SEEK_SET);
236 if (rv < 0) {
237 rc = EIO;
238 goto error;
239 }
240
241 fchunk.riffr = rr;
242 fchunk.ckstart = 0;
243 fchunk.cksize = fsize;
244
245 rc = riff_rchunk_start(&fchunk, riffck);
246 if (rc != EOK)
247 goto error;
248
249 *rrr = rr;
250 return EOK;
251error:
252 if (rr != NULL && rr->f != NULL)
253 fclose(rr->f);
254 free(rr);
255 return rc;
256}
257
258/** Close RIFF for reading.
259 *
260 * @param rr RIFF reader
261 * @return EOK on success, EIO on error.
262 */
263errno_t riff_rclose(riffr_t *rr)
264{
265 errno_t rc;
266
267 rc = fclose(rr->f);
268 free(rr);
269 return rc == 0 ? EOK : EIO;
270}
271
272/** Read uint32_t from RIFF file.
273 *
274 * @param rchunk RIFF chunk
275 * @param v Place to store value
276 * @return EOK on success, ELIMIT if at the end of parent chunk, EIO on error.
277 */
278errno_t riff_read_uint32(riff_rchunk_t *rchunk, uint32_t *v)
279{
280 uint32_t vle;
281 errno_t rc;
282 size_t nread;
283
284 rc = riff_read(rchunk, &vle, sizeof(vle), &nread);
285 if (rc != EOK)
286 return rc;
287
288 if (nread != sizeof(vle))
289 return ELIMIT;
290
291 *v = uint32_t_le2host(vle);
292 return EOK;
293}
294
295/** Start reading RIFF chunk.
296 *
297 * @param parent Parent chunk
298 * @param rchunk Pointer to chunk structure to fill in
299 *
300 * @return EOK on success, ELIMIT if at the end of parent chunk,
301 * EIO on error.
302 */
303errno_t riff_rchunk_start(riff_rchunk_t *parent, riff_rchunk_t *rchunk)
304{
305 errno_t rc;
306
307 rchunk->riffr = parent->riffr;
308 rchunk->ckstart = parent->riffr->pos + 8;
309 rc = riff_read_uint32(parent, &rchunk->ckid);
310 if (rc != EOK)
311 goto error;
312 rc = riff_read_uint32(parent, &rchunk->cksize);
313 if (rc != EOK)
314 goto error;
315
316 return EOK;
317error:
318 return rc;
319}
320
321/** Find and start reading RIFF chunk of with specific chunk ID.
322 * Other types of chunks are skipped.
323 *
324 * @param parent Parent chunk
325 * @param rchunk Pointer to chunk structure to fill in
326 *
327 * @return EOK on success, ENOENT chunk was not found and end was reached
328 * EIO on error.
329 */
330errno_t riff_rchunk_match(riff_rchunk_t *parent, riff_ckid_t ckid,
331 riff_rchunk_t *rchunk)
332{
333 errno_t rc;
334
335 while (true) {
336 rc = riff_rchunk_start(parent, rchunk);
337 if (rc == ELIMIT)
338 return ENOENT;
339 if (rc != EOK)
340 return rc;
341
342 if (rchunk->ckid == ckid)
343 break;
344
345 rc = riff_rchunk_end(rchunk);
346 if (rc != EOK)
347 return rc;
348 }
349
350 return EOK;
351}
352
353/** Find and start reading RIFF LIST chunk of specified type.
354 * Other chunks or LIST chunks of other type are skipped.
355 *
356 * @param parent Parent chunk
357 * @param rchunk Pointer to chunk structure to fill in
358 *
359 * @return EOK on success, ENOENT chunk was not found and end was reached
360 * EIO on error.
361 */
362errno_t riff_rchunk_list_match(riff_rchunk_t *parent, riff_ltype_t ltype,
363 riff_rchunk_t *rchunk)
364{
365 errno_t rc;
366 riff_ltype_t rltype;
367
368 while (true) {
369 rc = riff_rchunk_match(parent, CKID_LIST, rchunk);
370 if (rc == ELIMIT)
371 return ENOENT;
372 if (rc != EOK)
373 return rc;
374
375 rc = riff_read_uint32(parent, &rltype);
376 if (rc != EOK)
377 return rc;
378
379 if (rltype == ltype)
380 break;
381
382 rc = riff_rchunk_end(rchunk);
383 if (rc != EOK)
384 return rc;
385 }
386
387 return EOK;
388}
389
390/** Seek to position in chunk.
391 *
392 * @param rchunk RIFF chunk
393 * @param offset Offset
394 * @param whence SEEK_SET, SEEK_CUR or SEEK_END
395 * @return EOK on success or an error code
396 */
397errno_t riff_rchunk_seek(riff_rchunk_t *rchunk, long offset, int whence)
398{
399 long dest;
400 int rv;
401
402 switch (whence) {
403 case SEEK_SET:
404 dest = rchunk->ckstart + offset;
405 break;
406 case SEEK_END:
407 dest = rchunk->ckstart + rchunk->cksize + offset;
408 break;
409 case SEEK_CUR:
410 dest = rchunk->riffr->pos + offset;
411 break;
412 default:
413 return EINVAL;
414 }
415
416 if (dest < rchunk->ckstart || (unsigned long) dest >
417 (unsigned long) rchunk->ckstart + rchunk->cksize) {
418 return ELIMIT;
419 }
420
421 rv = fseek(rchunk->riffr->f, dest, SEEK_SET);
422 if (rv < 0)
423 return EIO;
424
425 rchunk->riffr->pos = dest;
426 return EOK;
427}
428
429/** Return chunk data size.
430 *
431 * @param rchunk RIFF chunk
432 * @return Pure data size (excluding type+size header) in bytes
433 */
434uint32_t riff_rchunk_size(riff_rchunk_t *rchunk)
435{
436 return rchunk->cksize;
437}
438
439/** Return file offset where chunk ends
440 *
441 * @param rchunk RIFF chunk
442 * @return File offset just after last data byte of the chunk
443 */
444static long riff_rchunk_get_end(riff_rchunk_t *rchunk)
445{
446 return rchunk->ckstart + rchunk->cksize;
447}
448
449/** Return file offset of first (non-padding) byte after end of chunk.
450 *
451 * @param rchunk RIFF chunk
452 * @return File offset of first non-padding byte after end of chunk
453 */
454static long riff_rchunk_get_ndpos(riff_rchunk_t *rchunk)
455{
456 long ckend;
457
458 ckend = riff_rchunk_get_end(rchunk);
459 if ((ckend % 2) != 0)
460 return ckend + 1;
461 else
462 return ckend;
463}
464
465/** Finish reading RIFF chunk.
466 *
467 * Seek to the first byte after end of chunk. It is allowed, though,
468 * to return to the chunk later, e.g. using riff_rchunk_seek(@a rchunk, ..).
469 *
470 * @param rchunk Chunk structure
471 * @return EOK on success, EIO on error.
472 */
473errno_t riff_rchunk_end(riff_rchunk_t *rchunk)
474{
475 long ckend;
476 uint8_t byte;
477 size_t nread;
478 errno_t rc;
479
480 ckend = riff_rchunk_get_ndpos(rchunk);
481 if (rchunk->riffr->pos < ckend &&
482 ckend <= rchunk->riffr->pos + 512) {
483 /* (Buffered) reading is faster than seeking */
484 while (rchunk->riffr->pos < ckend) {
485 rc = riff_read(rchunk, &byte, sizeof(byte), &nread);
486 if (rc != EOK)
487 return rc;
488
489 if (nread != sizeof(byte))
490 return EIO;
491 }
492
493 } else if (rchunk->riffr->pos != ckend) {
494 /* Need to seek (backwards or too far) */
495 if (fseek(rchunk->riffr->f, ckend, SEEK_SET) < 0)
496 return EIO;
497 rchunk->riffr->pos = ckend;
498 }
499
500 return EOK;
501}
502
503/** Read data from RIFF chunk.
504 *
505 * Attempt to read @a bytes bytes from the chunk. If there is less data
506 * left until the end of the chunk, less will be read. The actual number
507 * of bytes read is returned in @a *nbytes (can even be 0).
508 *
509 * @param rchunk RIFF chunk for reading
510 * @param buf Buffer to read to
511 * @param bytes Number of bytes to read
512 * @param nread Place to store number of bytes actually read
513 *
514 * @return EOK on success, ELIMIT if file position is not within @a rchunk,
515 * EIO on I/O error.
516 */
517errno_t riff_read(riff_rchunk_t *rchunk, void *buf, size_t bytes,
518 size_t *nread)
519{
520 long pos;
521 long ckend;
522 long toread;
523
524 pos = rchunk->riffr->pos;
525
526 ckend = riff_rchunk_get_end(rchunk);
527 if (pos < rchunk->ckstart || pos > ckend)
528 return ELIMIT;
529
530 toread = min(bytes, (size_t)(ckend - pos));
531 if (toread == 0) {
532 *nread = 0;
533 return EOK;
534 }
535
536 *nread = fread(buf, 1, toread, rchunk->riffr->f);
537 if (*nread == 0)
538 return EIO;
539
540 rchunk->riffr->pos += *nread;
541 return EOK;
542}
543
544/** @}
545 */
Note: See TracBrowser for help on using the repository browser.