]> git.ipfire.org Git - thirdparty/cups.git/blob - filter/rasterbench.c
Validate cupsBitsPerColor, cupsBitsPerPixel, and cupsBytesPerLine to avoid
[thirdparty/cups.git] / filter / rasterbench.c
1 /*
2 * "$Id$"
3 *
4 * Raster benchmark program for CUPS.
5 *
6 * Copyright 2007-2014 by Apple Inc.
7 * Copyright 1997-2006 by Easy Software Products.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 */
17
18 /*
19 * Include necessary headers...
20 */
21
22 #include <config.h>
23 #include <cups/raster.h>
24 #include <stdlib.h>
25 #include <sys/time.h>
26 #include <signal.h>
27 #include <unistd.h>
28 #include <sys/wait.h>
29
30
31 /*
32 * Constants...
33 */
34
35 #define TEST_WIDTH 1024
36 #define TEST_HEIGHT 1024
37 #define TEST_PAGES 16
38 #define TEST_PASSES 20
39
40
41 /*
42 * Local functions...
43 */
44
45 static double compute_median(double *secs);
46 static double get_time(void);
47 static void read_test(int fd);
48 static int run_read_test(void);
49 static void write_test(int fd, cups_mode_t mode);
50
51
52 /*
53 * 'main()' - Benchmark the raster read/write functions.
54 */
55
56 int /* O - Exit status */
57 main(int argc, /* I - Number of command-line args */
58 char *argv[]) /* I - Command-line arguments */
59 {
60 int i; /* Looping var */
61 int ras_fd, /* File descriptor for read process */
62 status; /* Exit status of read process */
63 double start_secs, /* Start time */
64 write_secs, /* Write time */
65 read_secs, /* Read time */
66 pass_secs[TEST_PASSES]; /* Total test times */
67 cups_mode_t mode; /* Write mode */
68
69
70 /*
71 * See if we have anything on the command-line...
72 */
73
74 if (argc > 2 || (argc == 2 && strcmp(argv[1], "-z")))
75 {
76 puts("Usage: rasterbench [-z]");
77 return (1);
78 }
79
80 mode = argc > 1 ? CUPS_RASTER_WRITE_COMPRESSED : CUPS_RASTER_WRITE;
81
82 /*
83 * Ignore SIGPIPE...
84 */
85
86 signal(SIGPIPE, SIG_IGN);
87
88 /*
89 * Run the tests several times to get a good average...
90 */
91
92 printf("Test read/write speed of %d pages, %dx%d pixels...\n\n",
93 TEST_PAGES, TEST_WIDTH, TEST_HEIGHT);
94 for (i = 0; i < TEST_PASSES; i ++)
95 {
96 printf("PASS %2d: ", i + 1);
97 fflush(stdout);
98
99 ras_fd = run_read_test();
100 start_secs = get_time();
101
102 write_test(ras_fd, mode);
103
104 write_secs = get_time();
105 printf(" %.3f write,", write_secs - start_secs);
106 fflush(stdout);
107
108 close(ras_fd);
109 wait(&status);
110
111 read_secs = get_time();
112 pass_secs[i] = read_secs - start_secs;
113 printf(" %.3f read, %.3f total\n", read_secs - write_secs, pass_secs[i]);
114 }
115
116 printf("\nMedian Total Time: %.3f seconds per document\n",
117 compute_median(pass_secs));
118
119 return (0);
120 }
121
122
123 /*
124 * 'compute_median()' - Compute the median time for a test.
125 */
126
127 static double /* O - Median time in seconds */
128 compute_median(double *secs) /* I - Array of time samples */
129 {
130 int i, j; /* Looping vars */
131 double temp; /* Swap variable */
132
133
134 /*
135 * Sort the array into ascending order using a quicky bubble sort...
136 */
137
138 for (i = 0; i < (TEST_PASSES - 1); i ++)
139 for (j = i + 1; j < TEST_PASSES; j ++)
140 if (secs[i] > secs[j])
141 {
142 temp = secs[i];
143 secs[i] = secs[j];
144 secs[j] = temp;
145 }
146
147 /*
148 * Return the average of the middle two samples...
149 */
150
151 return (0.5 * (secs[TEST_PASSES / 2 - 1] + secs[TEST_PASSES / 2]));
152 }
153
154
155 /*
156 * 'get_time()' - Get the current time in seconds.
157 */
158
159 static double /* O - Time in seconds */
160 get_time(void)
161 {
162 struct timeval curtime; /* Current time */
163
164
165 gettimeofday(&curtime, NULL);
166 return (curtime.tv_sec + 0.000001 * curtime.tv_usec);
167 }
168
169
170 /*
171 * 'read_test()' - Benchmark the raster read functions.
172 */
173
174 static void
175 read_test(int fd) /* I - File descriptor to read from */
176 {
177 unsigned y; /* Looping var */
178 cups_raster_t *r; /* Raster stream */
179 cups_page_header2_t header; /* Page header */
180 unsigned char buffer[8 * TEST_WIDTH];
181 /* Read buffer */
182
183
184 /*
185 * Test read speed...
186 */
187
188 if ((r = cupsRasterOpen(fd, CUPS_RASTER_READ)) == NULL)
189 {
190 perror("Unable to create raster input stream");
191 return;
192 }
193
194 while (cupsRasterReadHeader2(r, &header))
195 {
196 for (y = 0; y < header.cupsHeight; y ++)
197 cupsRasterReadPixels(r, buffer, header.cupsBytesPerLine);
198 }
199
200 cupsRasterClose(r);
201 }
202
203
204 /*
205 * 'run_read_test()' - Run the read test as a child process via pipes.
206 */
207
208 static int /* O - Standard input of child */
209 run_read_test(void)
210 {
211 int ras_pipes[2]; /* Raster data pipes */
212 int pid; /* Child process ID */
213
214
215 if (pipe(ras_pipes))
216 return (-1);
217
218 if ((pid = fork()) < 0)
219 {
220 /*
221 * Fork error - return -1 on error...
222 */
223
224 close(ras_pipes[0]);
225 close(ras_pipes[1]);
226
227 return (-1);
228 }
229 else if (pid == 0)
230 {
231 /*
232 * Child comes here - read data from the input pipe...
233 */
234
235 close(ras_pipes[1]);
236 read_test(ras_pipes[0]);
237 exit(0);
238 }
239 else
240 {
241 /*
242 * Parent comes here - return the output pipe...
243 */
244
245 close(ras_pipes[0]);
246 return (ras_pipes[1]);
247 }
248 }
249
250
251 /*
252 * 'write_test()' - Benchmark the raster write functions.
253 */
254
255 static void
256 write_test(int fd, /* I - File descriptor to write to */
257 cups_mode_t mode) /* I - Write mode */
258 {
259 unsigned page, x, y; /* Looping vars */
260 unsigned count; /* Number of bytes to set */
261 cups_raster_t *r; /* Raster stream */
262 cups_page_header2_t header; /* Page header */
263 unsigned char data[32][8 * TEST_WIDTH];
264 /* Raster data to write */
265
266
267 /*
268 * Create a combination of random data and repeated data to simulate
269 * text with some whitespace.
270 */
271
272 CUPS_SRAND(time(NULL));
273
274 memset(data, 0, sizeof(data));
275
276 for (y = 0; y < 28; y ++)
277 {
278 for (x = CUPS_RAND() & 127, count = (CUPS_RAND() & 15) + 1;
279 x < sizeof(data[0]);
280 x ++, count --)
281 {
282 if (count <= 0)
283 {
284 x += (CUPS_RAND() & 15) + 1;
285 count = (CUPS_RAND() & 15) + 1;
286
287 if (x >= sizeof(data[0]))
288 break;
289 }
290
291 data[y][x] = (unsigned char)CUPS_RAND();
292 }
293 }
294
295 /*
296 * Test write speed...
297 */
298
299 if ((r = cupsRasterOpen(fd, mode)) == NULL)
300 {
301 perror("Unable to create raster output stream");
302 return;
303 }
304
305 for (page = 0; page < TEST_PAGES; page ++)
306 {
307 memset(&header, 0, sizeof(header));
308 header.cupsWidth = TEST_WIDTH;
309 header.cupsHeight = TEST_HEIGHT;
310 header.cupsBytesPerLine = TEST_WIDTH;
311
312 if (page & 1)
313 {
314 header.cupsBytesPerLine *= 4;
315 header.cupsColorSpace = CUPS_CSPACE_CMYK;
316 header.cupsColorOrder = CUPS_ORDER_CHUNKED;
317 }
318 else
319 {
320 header.cupsColorSpace = CUPS_CSPACE_K;
321 header.cupsColorOrder = CUPS_ORDER_BANDED;
322 }
323
324 if (page & 2)
325 {
326 header.cupsBytesPerLine *= 2;
327 header.cupsBitsPerColor = 16;
328 header.cupsBitsPerPixel = (page & 1) ? 64 : 16;
329 }
330 else
331 {
332 header.cupsBitsPerColor = 8;
333 header.cupsBitsPerPixel = (page & 1) ? 32 : 8;
334 }
335
336 cupsRasterWriteHeader2(r, &header);
337
338 for (y = 0; y < TEST_HEIGHT; y ++)
339 cupsRasterWritePixels(r, data[y & 31], header.cupsBytesPerLine);
340 }
341
342 cupsRasterClose(r);
343 }
344
345
346 /*
347 * End of "$Id$".
348 */