Home | History | Annotate | Download | only in libtests
      1 /* timepng.c
      2  *
      3  * Copyright (c) 2013 John Cunningham Bowler
      4  *
      5  * Last changed in libpng 1.6.1 [March 28, 2013]
      6  *
      7  * This code is released under the libpng license.
      8  * For conditions of distribution and use, see the disclaimer
      9  * and license in png.h
     10  *
     11  * Load an arbitrary number of PNG files (from the command line, or, if there
     12  * are no arguments on the command line, from stdin) then run a time test by
     13  * reading each file by row.  The test does nothing with the read result and
     14  * does no transforms.  The only output is a time as a floating point number of
     15  * seconds with 9 decimal digits.
     16  */
     17 #define _POSIX_C_SOURCE 199309L /* for clock_gettime */
     18 
     19 #include <stdlib.h>
     20 #include <stdio.h>
     21 #include <string.h>
     22 
     23 #include <time.h>
     24 
     25 #if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
     26 #  include <config.h>
     27 #endif
     28 
     29 /* Define the following to use this test against your installed libpng, rather
     30  * than the one being built here:
     31  */
     32 #ifdef PNG_FREESTANDING_TESTS
     33 #  include <png.h>
     34 #else
     35 #  include "../../png.h"
     36 #endif
     37 
     38 static int read_png(FILE *fp)
     39 {
     40    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
     41    png_infop info_ptr = NULL;
     42    png_bytep row = NULL, display = NULL;
     43 
     44    if (png_ptr == NULL)
     45       return 0;
     46 
     47    if (setjmp(png_jmpbuf(png_ptr)))
     48    {
     49       png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
     50       if (row != NULL) free(row);
     51       if (display != NULL) free(display);
     52       return 0;
     53    }
     54 
     55    png_init_io(png_ptr, fp);
     56 
     57    info_ptr = png_create_info_struct(png_ptr);
     58    if (info_ptr == NULL)
     59       png_error(png_ptr, "OOM allocating info structure");
     60 
     61    png_read_info(png_ptr, info_ptr);
     62 
     63    {
     64       png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
     65 
     66       row = malloc(rowbytes);
     67       display = malloc(rowbytes);
     68 
     69       if (row == NULL || display == NULL)
     70          png_error(png_ptr, "OOM allocating row buffers");
     71 
     72       {
     73          png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
     74          int passes = png_set_interlace_handling(png_ptr);
     75          int pass;
     76 
     77          png_start_read_image(png_ptr);
     78 
     79          for (pass = 0; pass < passes; ++pass)
     80          {
     81             png_uint_32 y = height;
     82 
     83             /* NOTE: this trashes the row each time; interlace handling won't
     84              * work, but this avoids memory thrashing for speed testing.
     85              */
     86             while (y-- > 0)
     87                png_read_row(png_ptr, row, display);
     88          }
     89       }
     90    }
     91 
     92    /* Make sure to read to the end of the file: */
     93    png_read_end(png_ptr, info_ptr);
     94    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
     95    free(row);
     96    free(display);
     97    return 1;
     98 }
     99 
    100 static int mytime(struct timespec *t)
    101 {
    102    /* Do the timing using clock_gettime and the per-process timer. */
    103    if (!clock_gettime(CLOCK_PROCESS_CPUTIME_ID, t))
    104       return 1;
    105 
    106    perror("CLOCK_PROCESS_CPUTIME_ID");
    107    fprintf(stderr, "timepng: could not get the time\n");
    108    return 0;
    109 }
    110 
    111 static int perform_one_test(FILE *fp, int nfiles)
    112 {
    113    int i;
    114    struct timespec before, after;
    115 
    116    /* Clear out all errors: */
    117    rewind(fp);
    118 
    119    if (mytime(&before))
    120    {
    121       for (i=0; i<nfiles; ++i)
    122       {
    123          if (read_png(fp))
    124          {
    125             if (ferror(fp))
    126             {
    127                perror("temporary file");
    128                fprintf(stderr, "file %d: error reading PNG data\n", i);
    129                return 0;
    130             }
    131          }
    132 
    133          else
    134          {
    135             perror("temporary file");
    136             fprintf(stderr, "file %d: error from libpng\n", i);
    137             return 0;
    138          }
    139       }
    140    }
    141 
    142    else
    143       return 0;
    144 
    145    if (mytime(&after))
    146    {
    147       /* Work out the time difference and print it - this is the only output,
    148        * so flush it immediately.
    149        */
    150       unsigned long s = after.tv_sec - before.tv_sec;
    151       long ns = after.tv_nsec - before.tv_nsec;
    152 
    153       if (ns < 0)
    154       {
    155          --s;
    156          ns += 1000000000;
    157 
    158          if (ns < 0)
    159          {
    160             fprintf(stderr, "timepng: bad clock from kernel\n");
    161             return 0;
    162          }
    163       }
    164 
    165       printf("%lu.%.9ld\n", s, ns);
    166       fflush(stdout);
    167       if (ferror(stdout))
    168       {
    169          fprintf(stderr, "timepng: error writing output\n");
    170          return 0;
    171       }
    172 
    173       /* Successful return */
    174       return 1;
    175    }
    176 
    177    else
    178       return 0;
    179 }
    180 
    181 static int add_one_file(FILE *fp, char *name)
    182 {
    183    FILE *ip = fopen(name, "rb");
    184 
    185    if (ip != NULL)
    186    {
    187       int ch;
    188       for (;;)
    189       {
    190          ch = getc(ip);
    191          if (ch == EOF) break;
    192          putc(ch, fp);
    193       }
    194 
    195       if (ferror(ip))
    196       {
    197          perror(name);
    198          fprintf(stderr, "%s: read error\n", name);
    199          return 0;
    200       }
    201 
    202       (void)fclose(ip);
    203 
    204       if (ferror(fp))
    205       {
    206          perror("temporary file");
    207          fprintf(stderr, "temporary file write error\n");
    208          return 0;
    209       }
    210    }
    211 
    212    else
    213    {
    214       perror(name);
    215       fprintf(stderr, "%s: open failed\n", name);
    216       return 0;
    217    }
    218 
    219    return 1;
    220 }
    221 
    222 int main(int argc, char **argv)
    223 {
    224    int ok = 0;
    225    FILE *fp = tmpfile();
    226 
    227    if (fp != NULL)
    228    {
    229       int err = 0;
    230       int nfiles = 0;
    231 
    232       if (argc > 1)
    233       {
    234          int i;
    235 
    236          for (i=1; i<argc; ++i)
    237          {
    238             if (add_one_file(fp, argv[i]))
    239                ++nfiles;
    240 
    241             else
    242             {
    243                err = 1;
    244                break;
    245             }
    246          }
    247       }
    248 
    249       else
    250       {
    251          char filename[FILENAME_MAX+1];
    252 
    253          while (fgets(filename, FILENAME_MAX+1, stdin))
    254          {
    255             size_t len = strlen(filename);
    256 
    257             if (filename[len-1] == '\n')
    258             {
    259                filename[len-1] = 0;
    260                if (add_one_file(fp, filename))
    261                   ++nfiles;
    262 
    263                else
    264                {
    265                   err = 1;
    266                   break;
    267                }
    268             }
    269 
    270             else
    271             {
    272                fprintf(stderr, "timepng: truncated file name ...%s\n",
    273                   filename+len-32);
    274                err = 1;
    275                break;
    276             }
    277          }
    278 
    279          if (ferror(stdin))
    280          {
    281             fprintf(stderr, "timepng: stdin: read error\n");
    282             err = 1;
    283          }
    284       }
    285 
    286       if (!err)
    287       {
    288          if (nfiles > 0)
    289             ok = perform_one_test(fp, nfiles);
    290 
    291          else
    292             fprintf(stderr, "usage: timepng {files} or ls files | timepng\n");
    293       }
    294 
    295       (void)fclose(fp);
    296    }
    297 
    298    else
    299       fprintf(stderr, "timepng: could not open temporary file\n");
    300 
    301    /* Exit code 0 on success. */
    302    return ok == 0;
    303 }
    304