VirtualBox

source: vbox/trunk/src/libs/libpng-1.6.42/contrib/libtests/timepng.c@ 103316

Last change on this file since 103316 was 103316, checked in by vboxsync, 12 months ago

libpng-1.6.42: Applied and adjusted our libpng changes to 1.6.42. bugref:8515

  • Property svn:eol-style set to native
File size: 15.1 KB
Line 
1
2/* timepng.c
3 *
4 * Copyright (c) 2013,2016 John Cunningham Bowler
5 *
6 * This code is released under the libpng license.
7 * For conditions of distribution and use, see the disclaimer
8 * and license in png.h
9 *
10 * Load an arbitrary number of PNG files (from the command line, or, if there
11 * are no arguments on the command line, from stdin) then run a time test by
12 * reading each file by row or by image (possibly with transforms in the latter
13 * case). The only output is a time as a floating point number of seconds with
14 * 9 decimal digits.
15 */
16#define _POSIX_C_SOURCE 199309L /* for clock_gettime */
17
18#include <stdlib.h>
19#include <stdio.h>
20#include <string.h>
21#include <errno.h>
22#include <limits.h>
23
24#include <time.h>
25
26#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
27# include <config.h>
28#endif
29
30/* Define the following to use this test against your installed libpng, rather
31 * than the one being built here:
32 */
33#ifdef PNG_FREESTANDING_TESTS
34# include <png.h>
35#else
36# include "../../png.h"
37#endif
38
39/* The following is to support direct compilation of this file as C++ */
40#ifdef __cplusplus
41# define voidcast(type, value) static_cast<type>(value)
42#else
43# define voidcast(type, value) (value)
44#endif /* __cplusplus */
45
46/* 'CLOCK_PROCESS_CPUTIME_ID' is one of the clock timers for clock_gettime. It
47 * need not be supported even when clock_gettime is available. It returns the
48 * 'CPU' time the process has consumed. 'CPU' time is assumed to include time
49 * when the CPU is actually blocked by a pending cache fill but not time
50 * waiting for page faults. The attempt is to get a measure of the actual time
51 * the implementation takes to read a PNG ignoring the potentially very large IO
52 * overhead.
53 */
54#if defined (CLOCK_PROCESS_CPUTIME_ID) && defined(PNG_STDIO_SUPPORTED) &&\
55 defined(PNG_EASY_ACCESS_SUPPORTED) &&\
56 (PNG_LIBPNG_VER >= 10700 ? defined(PNG_READ_PNG_SUPPORTED) :\
57 defined (PNG_SEQUENTIAL_READ_SUPPORTED) &&\
58 defined(PNG_INFO_IMAGE_SUPPORTED))
59
60typedef struct
61{
62 FILE *input;
63 FILE *output;
64} io_data;
65
66static PNG_CALLBACK(void, read_and_copy,
67 (png_structp png_ptr, png_bytep buffer, size_t cb))
68{
69 io_data *io = (io_data*)png_get_io_ptr(png_ptr);
70
71 if (fread(buffer, cb, 1, io->input) != 1)
72 png_error(png_ptr, strerror(errno));
73
74 if (fwrite(buffer, cb, 1, io->output) != 1)
75 {
76 perror("temporary file");
77 fprintf(stderr, "temporary file PNG write failed\n");
78 exit(1);
79 }
80}
81
82static void read_by_row(png_structp png_ptr, png_infop info_ptr,
83 FILE *write_ptr, FILE *read_ptr)
84{
85 /* These don't get freed on error, this is fine; the program immediately
86 * exits.
87 */
88 png_bytep row = NULL, display = NULL;
89 io_data io_copy;
90
91 if (write_ptr != NULL)
92 {
93 /* Set up for a copy to the temporary file: */
94 io_copy.input = read_ptr;
95 io_copy.output = write_ptr;
96 png_set_read_fn(png_ptr, &io_copy, read_and_copy);
97 }
98
99 png_read_info(png_ptr, info_ptr);
100
101 {
102 size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
103
104 row = voidcast(png_bytep,malloc(rowbytes));
105 display = voidcast(png_bytep,malloc(rowbytes));
106
107 if (row == NULL || display == NULL)
108 png_error(png_ptr, "OOM allocating row buffers");
109
110 {
111 png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
112 int passes = png_set_interlace_handling(png_ptr);
113 int pass;
114
115 png_start_read_image(png_ptr);
116
117 for (pass = 0; pass < passes; ++pass)
118 {
119 png_uint_32 y = height;
120
121 /* NOTE: this trashes the row each time; interlace handling won't
122 * work, but this avoids memory thrashing for speed testing and is
123 * somewhat representative of an application that works row-by-row.
124 */
125 while (y-- > 0)
126 png_read_row(png_ptr, row, display);
127 }
128 }
129 }
130
131 /* Make sure to read to the end of the file: */
132 png_read_end(png_ptr, info_ptr);
133
134 /* Free this up: */
135 free(row);
136 free(display);
137}
138
139static PNG_CALLBACK(void, no_warnings, (png_structp png_ptr,
140 png_const_charp warning))
141{
142 (void)png_ptr;
143 (void)warning;
144}
145
146static int read_png(FILE *fp, png_int_32 transforms, FILE *write_file)
147{
148 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,
149 no_warnings);
150 png_infop info_ptr = NULL;
151
152 if (png_ptr == NULL)
153 return 0;
154
155 if (setjmp(png_jmpbuf(png_ptr)))
156 {
157 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
158 return 0;
159 }
160
161# ifdef PNG_BENIGN_ERRORS_SUPPORTED
162 png_set_benign_errors(png_ptr, 1/*allowed*/);
163# endif
164 png_init_io(png_ptr, fp);
165
166 info_ptr = png_create_info_struct(png_ptr);
167
168 if (info_ptr == NULL)
169 png_error(png_ptr, "OOM allocating info structure");
170
171 if (transforms < 0)
172 read_by_row(png_ptr, info_ptr, write_file, fp);
173
174 else
175 png_read_png(png_ptr, info_ptr, transforms, NULL/*params*/);
176
177 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
178 return 1;
179}
180
181static int mytime(struct timespec *t)
182{
183 /* Do the timing using clock_gettime and the per-process timer. */
184 if (!clock_gettime(CLOCK_PROCESS_CPUTIME_ID, t))
185 return 1;
186
187 perror("CLOCK_PROCESS_CPUTIME_ID");
188 fprintf(stderr, "timepng: could not get the time\n");
189 return 0;
190}
191
192static int perform_one_test(FILE *fp, int nfiles, png_int_32 transforms)
193{
194 int i;
195 struct timespec before, after;
196
197 /* Clear out all errors: */
198 rewind(fp);
199
200 if (mytime(&before))
201 {
202 for (i=0; i<nfiles; ++i)
203 {
204 if (read_png(fp, transforms, NULL/*write*/))
205 {
206 if (ferror(fp))
207 {
208 perror("temporary file");
209 fprintf(stderr, "file %d: error reading PNG data\n", i);
210 return 0;
211 }
212 }
213
214 else
215 {
216 perror("temporary file");
217 fprintf(stderr, "file %d: error from libpng\n", i);
218 return 0;
219 }
220 }
221 }
222
223 else
224 return 0;
225
226 if (mytime(&after))
227 {
228 /* Work out the time difference and print it - this is the only output,
229 * so flush it immediately.
230 */
231 unsigned long s = after.tv_sec - before.tv_sec;
232 long ns = after.tv_nsec - before.tv_nsec;
233
234 if (ns < 0)
235 {
236 --s;
237 ns += 1000000000;
238
239 if (ns < 0)
240 {
241 fprintf(stderr, "timepng: bad clock from kernel\n");
242 return 0;
243 }
244 }
245
246 printf("%lu.%.9ld\n", s, ns);
247 fflush(stdout);
248 if (ferror(stdout))
249 {
250 fprintf(stderr, "timepng: error writing output\n");
251 return 0;
252 }
253
254 /* Successful return */
255 return 1;
256 }
257
258 else
259 return 0;
260}
261
262static int add_one_file(FILE *fp, char *name)
263{
264 FILE *ip = fopen(name, "rb");
265
266 if (ip != NULL)
267 {
268 /* Read the file using libpng; this detects errors and also deals with
269 * files which contain data beyond the end of the file.
270 */
271 int ok = 0;
272 fpos_t pos;
273
274 if (fgetpos(fp, &pos))
275 {
276 /* Fatal error reading the start: */
277 perror("temporary file");
278 fprintf(stderr, "temporary file fgetpos error\n");
279 exit(1);
280 }
281
282 if (read_png(ip, -1/*by row*/, fp/*output*/))
283 {
284 if (ferror(ip))
285 {
286 perror(name);
287 fprintf(stderr, "%s: read error\n", name);
288 }
289
290 else
291 ok = 1; /* read ok */
292 }
293
294 else
295 fprintf(stderr, "%s: file not added\n", name);
296
297 (void)fclose(ip);
298
299 /* An error in the output is fatal; exit immediately: */
300 if (ferror(fp))
301 {
302 perror("temporary file");
303 fprintf(stderr, "temporary file write error\n");
304 exit(1);
305 }
306
307 if (ok)
308 return 1;
309
310 /* Did not read the file successfully, simply rewind the temporary
311 * file. This must happen after the ferror check above to avoid clearing
312 * the error.
313 */
314 if (fsetpos(fp, &pos))
315 {
316 perror("temporary file");
317 fprintf(stderr, "temporary file fsetpos error\n");
318 exit(1);
319 }
320 }
321
322 else
323 {
324 /* file open error: */
325 perror(name);
326 fprintf(stderr, "%s: open failed\n", name);
327 }
328
329 return 0; /* file not added */
330}
331
332static void
333usage(FILE *fp)
334{
335 if (fp != NULL) fclose(fp);
336
337 fprintf(stderr,
338"Usage:\n"
339" timepng --assemble <assembly> {files}\n"
340" Read the files into <assembly>, output the count. Options are ignored.\n"
341" timepng --dissemble <assembly> <count> [options]\n"
342" Time <count> files from <assembly>, additional files may not be given.\n"
343" Otherwise:\n"
344" Read the files into a temporary file and time the decode\n"
345"Transforms:\n"
346" --by-image: read by image with png_read_png\n"
347" --<transform>: implies by-image, use PNG_TRANSFORM_<transform>\n"
348" Otherwise: read by row using png_read_row (to a single row buffer)\n"
349 /* ISO C90 string length max 509 */);fprintf(stderr,
350"{files}:\n"
351" PNG files to copy into the assembly and time. Invalid files are skipped\n"
352" with appropriate error messages. If no files are given the list of files\n"
353" is read from stdin with each file name terminated by a newline\n"
354"Output:\n"
355" For --assemble the output is the name of the assembly file followed by the\n"
356" count of the files it contains; the arguments for --dissemble. Otherwise\n"
357" the output is the total decode time in seconds.\n");
358
359 exit(99);
360}
361
362int main(int argc, char **argv)
363{
364 int ok = 0;
365 int err = 0;
366 int nfiles = 0;
367 int transforms = -1; /* by row */
368 const char *assembly = NULL;
369 FILE *fp;
370
371 if (argc > 2 && strcmp(argv[1], "--assemble") == 0)
372 {
373 /* Just build the test file, argv[2] is the file name. */
374 assembly = argv[2];
375 fp = fopen(assembly, "wb");
376 if (fp == NULL)
377 {
378 perror(assembly);
379 fprintf(stderr, "timepng --assemble %s: could not open for write\n",
380 assembly);
381 usage(NULL);
382 }
383
384 argv += 2;
385 argc -= 2;
386 }
387
388 else if (argc > 3 && strcmp(argv[1], "--dissemble") == 0)
389 {
390 fp = fopen(argv[2], "rb");
391
392 if (fp == NULL)
393 {
394 perror(argv[2]);
395 fprintf(stderr, "timepng --dissemble %s: could not open for read\n",
396 argv[2]);
397 usage(NULL);
398 }
399
400 nfiles = atoi(argv[3]);
401 if (nfiles <= 0)
402 {
403 fprintf(stderr,
404 "timepng --dissemble <file> <count>: %s is not a count\n",
405 argv[3]);
406 exit(99);
407 }
408#ifdef __COVERITY__
409 else
410 {
411 nfiles &= PNG_UINT_31_MAX;
412 }
413#endif
414
415 argv += 3;
416 argc -= 3;
417 }
418
419 else /* Else use a temporary file */
420 {
421#ifndef __COVERITY__
422 fp = tmpfile();
423#else
424 /* Experimental. Coverity says tmpfile() is insecure because it
425 * generates predictable names.
426 *
427 * It is possible to satisfy Coverity by using mkstemp(); however,
428 * any platform supporting mkstemp() undoubtedly has a secure tmpfile()
429 * implementation as well, and doesn't need the fix. Note that
430 * the fix won't work on platforms that don't support mkstemp().
431 *
432 * https://www.securecoding.cert.org/confluence/display/c/
433 * FIO21-C.+Do+not+create+temporary+files+in+shared+directories
434 * says that most historic implementations of tmpfile() provide
435 * only a limited number of possible temporary file names
436 * (usually 26) before file names are recycled. That article also
437 * provides a secure solution that unfortunately depends upon mkstemp().
438 */
439 char tmpfile[] = "timepng-XXXXXX";
440 int filedes;
441 umask(0177);
442 filedes = mkstemp(tmpfile);
443 if (filedes < 0)
444 fp = NULL;
445 else
446 {
447 fp = fdopen(filedes,"w+");
448 /* Hide the filename immediately and ensure that the file does
449 * not exist after the program ends
450 */
451 (void) unlink(tmpfile);
452 }
453#endif
454
455 if (fp == NULL)
456 {
457 perror("tmpfile");
458 fprintf(stderr, "timepng: could not open the temporary file\n");
459 exit(1); /* not a user error */
460 }
461 }
462
463 /* Handle the transforms: */
464 while (argc > 1 && argv[1][0] == '-' && argv[1][1] == '-')
465 {
466 const char *opt = *++argv + 2;
467
468 --argc;
469
470 /* Transforms turn on the by-image processing and maybe set some
471 * transforms:
472 */
473 if (transforms == -1)
474 transforms = PNG_TRANSFORM_IDENTITY;
475
476 if (strcmp(opt, "by-image") == 0)
477 {
478 /* handled above */
479 }
480
481# define OPT(name) else if (strcmp(opt, #name) == 0)\
482 transforms |= PNG_TRANSFORM_ ## name
483
484 OPT(STRIP_16);
485 OPT(STRIP_ALPHA);
486 OPT(PACKING);
487 OPT(PACKSWAP);
488 OPT(EXPAND);
489 OPT(INVERT_MONO);
490 OPT(SHIFT);
491 OPT(BGR);
492 OPT(SWAP_ALPHA);
493 OPT(SWAP_ENDIAN);
494 OPT(INVERT_ALPHA);
495 OPT(STRIP_FILLER);
496 OPT(STRIP_FILLER_BEFORE);
497 OPT(STRIP_FILLER_AFTER);
498 OPT(GRAY_TO_RGB);
499 OPT(EXPAND_16);
500 OPT(SCALE_16);
501
502 else
503 {
504 fprintf(stderr, "timepng %s: unrecognized transform\n", opt);
505 usage(fp);
506 }
507 }
508
509 /* Handle the files: */
510 if (argc > 1 && nfiles > 0)
511 usage(fp); /* Additional files not valid with --dissemble */
512
513 else if (argc > 1)
514 {
515 int i;
516
517 for (i=1; i<argc; ++i)
518 {
519 if (nfiles == INT_MAX)
520 {
521 fprintf(stderr, "%s: skipped, too many files\n", argv[i]);
522 break;
523 }
524
525 else if (add_one_file(fp, argv[i]))
526 ++nfiles;
527 }
528 }
529
530 else if (nfiles == 0) /* Read from stdin without --dissemble */
531 {
532 char filename[FILENAME_MAX+1];
533
534 while (fgets(filename, FILENAME_MAX+1, stdin))
535 {
536 size_t len = strlen(filename);
537
538 if (filename[len-1] == '\n')
539 {
540 filename[len-1] = 0;
541 if (nfiles == INT_MAX)
542 {
543 fprintf(stderr, "%s: skipped, too many files\n", filename);
544 break;
545 }
546
547 else if (add_one_file(fp, filename))
548 ++nfiles;
549 }
550
551 else
552 {
553 fprintf(stderr, "timepng: file name too long: ...%s\n",
554 filename+len-32);
555 err = 1;
556 break;
557 }
558 }
559
560 if (ferror(stdin))
561 {
562 fprintf(stderr, "timepng: stdin: read error\n");
563 err = 1;
564 }
565 }
566
567 /* Perform the test, or produce the --assemble output: */
568 if (!err)
569 {
570 if (nfiles > 0)
571 {
572 if (assembly != NULL)
573 {
574 if (fflush(fp) && !ferror(fp) && fclose(fp))
575 {
576 perror(assembly);
577 fprintf(stderr, "%s: close failed\n", assembly);
578 }
579
580 else
581 {
582 printf("%s %d\n", assembly, nfiles);
583 fflush(stdout);
584 ok = !ferror(stdout);
585 }
586 }
587
588 else
589 {
590 ok = perform_one_test(fp, nfiles, transforms);
591 (void)fclose(fp);
592 }
593 }
594
595 else
596 usage(fp);
597 }
598
599 else
600 (void)fclose(fp);
601
602 /* Exit code 0 on success. */
603 return ok == 0;
604}
605#else /* !sufficient support */
606int main(void) { return 77; }
607#endif /* !sufficient support */
Note: See TracBrowser for help on using the repository browser.

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