OSDN Git Service

binding with libharu.
[putex/putex.git] / src / texsourc / lib / libpng / contrib / tools / chunkhash.c
1 /* chunkhash.c -- build a perfect hash code for the chunk names on stdin.
2  *
3  * Last changed in libpng 1.7.0 [(PENDING RELEASE)]
4  *
5  * COPYRIGHT: Written by John Cunningham Bowler, 2014.
6  * To the extent possible under law, the author has waived all copyright and
7  * related or neighboring rights to this work.  This work is published from:
8  * United States.
9  *
10  * Feed this program all the known chunks that libpng must recognize.  It will
11  * generate an appropriate hash key for the macro in pngpriv.h  To generate the
12  * list of chunks currently defined in png.h do this:
13  *
14  *    sed -n -e 's/^#define png_\(....\) PNG_U32.*$/\1/p' png.h
15  *
16  * An alternative to using this program is to pipe the output of the above
17  * through gperf, however the code generated by perf is somewhat more verbose
18  * and it doesn't generate as good a result (however it's a heck of a lot faster
19  * than this program!)
20  */
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <png.h> /* for the libpng types for this platform */
25
26 /* The machine generated file is checked in, so this works and it obtains the
27  * definition of the PNG_CHUNK_HASH macro.  To change this definition only ever
28  * alter the strings below!
29  */
30 #include "chunkhash.h"
31
32 /* The best_ variables are local variables defined in the functions below.  When
33  * used in libpng code, however, they are just constants.
34  */
35 #define PNG_CHUNK_HASH_MASK best_mask
36 #define PNG_CHUNK_HASH_C0 best_c[0]
37 #define PNG_CHUNK_HASH_C1 best_c[1]
38 #define PNG_CHUNK_HASH_C2 best_c[2]
39 #define PNG_CHUNK_HASH_C3 best_c[3]
40
41 /* These strings contain the C text to copy into the new version of
42  * contrib/tools/chunkhash.c
43  */
44 static const char *strings[] = {
45 "/* chunkhash.h -- a perfect hash code for the chunk names in png.h",
46 " *",
47 " * Last changed in libpng 1.7.0 [(PENDING RELEASE)]",
48 " *",
49 " * THIS IS A MACHINE GENERATED FILE.  See contrib/tools/chunkhash.c for",
50 " * copyright and other information.",
51 " *",
52 " * USAGE:  To include the PNG_CHUNK_HASH macro and associated definitions:",
53 " *",
54 " * #define PNG_CHUNKHASH_DEFS",
55 " * #include \"contrib/tools/chunkhash.h\"",
56 " *",
57 " * To define the png_chunk_hash array used by the macro:",
58 " *",
59 " * #define PNG_CHUNKHASH_CODE",
60 " * #include \"contrib/tools/chunkhash.h\"",
61 " *",
62 " * One or both of the defines must be given except when building chunkhash",
63 " * itself.",
64 " */",
65 "#ifdef PNG_CHUNKHASH_DEFS",
66 "#ifndef PNG_CHUNKHASH_H",
67 "#define PNG_CHUNKHASH_H",
68 "/* A perfect hash code - returns a value 0..(PNG_KNOWN_CHUNK_COUNT-1) and is",
69 " * generated by the ridiculously simple program in contrib/tools/chunkhash.c",
70 " *",
71 " * The hash code used here multiplies each byte by a different constant to",
72 " * return a single number:",
73 " *",
74 " *    b0 * c[0] + b1 * [c1] + b2 * c[2] + b3 * c[3]",
75 " *",
76 " * The values of the constants are found by search using the a table of",
77 " * primes, including 0, and may be (in fact are at present) 0 for some of the",
78 " * bytes, the compiler is expected to optimize multiply by zero, or one!",
79 " *",
80 " * The lookup table reduces the sparse result of the hash calculation to the",
81 " * correct index values.  The chunks are indexed in the string order of the",
82 " * png_uint_32 chunk names.",
83 " */",
84 0, /* HEADER definitions go here */
85 "",
86 "extern const png_byte png_chunk_hash[PNG_CHUNK_HASH_MASK+1];",
87 "#endif /* !PNG_CHUNKHASH_H */",
88 "#endif /* PNG_CHUNKHASH_DEFS */",
89 "",
90 "#ifndef PNG_CHUNK_HASH",
91 "#define PNG_CHUNK_HASH(chunk) (png_chunk_hash[PNG_CHUNK_HASH_MASK & (\\",
92 "      ((chunk) >> 24) * PNG_CHUNK_HASH_C0 +\\",
93 "      ((chunk) >> 16) * PNG_CHUNK_HASH_C1 +\\",
94 "      ((chunk) >>  8) * PNG_CHUNK_HASH_C2 +\\",
95 "      ((chunk)      ) * PNG_CHUNK_HASH_C3)])",
96 "#endif",
97 "",
98 "#ifdef PNG_CHUNKHASH_CODE",
99 "#ifndef PNG_CHUNKHASH_C",
100 "#define PNG_CHUNKHASH_C",
101 "const png_byte png_chunk_hash[PNG_CHUNK_HASH_MASK+1] = {",
102 0, /* png.c definitions go here */
103 "};",
104 "#endif /* !PNG_CHUNKHASH_C */",
105 "#endif /* PNG_CHUNKHASH_CODE */",
106 0 /* end of file */
107 };
108
109 #define CHUNK_COUNT_MAX 32
110    /* If necessary this is easy to increase; just don't use a png_uint_32
111     * bitmask below, however it doesn't seem likely that the limit will be hit
112     * any time soon.
113     */
114
115 static const png_byte
116 chunk_hash[256] =
117 {
118    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
119    22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
120    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
121    60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
122    79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
123    98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
124    114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
125    129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
126    144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158,
127    159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173,
128    174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188,
129    189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203,
130    204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218,
131    219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233,
132    234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248,
133    249, 250, 251, 252, 253, 254, 255
134 };
135
136 static void
137 error(const char *string)
138 {
139    fprintf(stderr, "chunkhash: %s\n", string);
140    exit(1);
141 }
142
143 /* Return a hash code for the given chunk; this is the same as the pngpriv.h
144  * macro, but parameterized.  The names of the parameters have to be chosen to
145  * match the above #defines.
146  */
147 static png_uint_32
148 test_hash(png_uint_32 chunk, const unsigned int *best_c, png_uint_32 best_mask)
149 {
150 #  define png_chunk_hash chunk_hash /* prevents lookup */
151       return PNG_CHUNK_HASH(chunk);
152 #  undef png_chunk_hash
153 }
154
155 static void
156 print_chunks(const png_uint_32 *chunks, unsigned int nchunks, png_uint_32 first,
157    png_uint_32 last, const unsigned int *best_c, png_uint_32 best_mask,
158    const png_byte *lut)
159 {
160    /* 'last' is the last code to print plus 1, this is used to detect duplicates
161     * (which should not happen - if there are any it's an internal error!)
162     */
163    if (nchunks > 0)
164    {
165       /* Recursive algorithm; find the hash code of the next chunk, then print
166        * all remaining chunks with codes in the range first..code (including
167        * duplicates) and after print all remaining chunks with codes in the
168        * range (code+1)..last
169        */
170       const png_uint_32 chunk = *chunks++;
171 #     define png_chunk_hash lut
172          const png_uint_32 code = PNG_CHUNK_HASH(chunk);
173 #     undef png_chunk_hash
174
175       --nchunks;
176
177       /* The code might be out of the print range */
178       if (code >= first && code <= last)
179       {
180          if (code >= first) /* not time yet */
181             print_chunks(chunks, nchunks, first, code, best_c, best_mask, lut);
182
183          printf("#define PNG_CHUNK_%c%c%c%c_TAG %lu%s\n", (char)(chunk>>24),
184             (char)(chunk>>16), (char)(chunk>>8), (char)chunk,
185             (unsigned long)code, code == last ? " /* DUPLICATE */" : "");
186
187          if (code < last) /* still some to go */
188             print_chunks(chunks, nchunks, code+1, last, best_c, best_mask, lut);
189       }
190
191       else
192          print_chunks(chunks, nchunks, first, last, best_c, best_mask, lut);
193    }
194 }
195
196 static unsigned int primes[] =
197 {
198    0, 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
199    71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149,
200    151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
201    233, 239, 241, 251
202 };
203
204 #define NPRIMES ((sizeof primes)/(sizeof primes[0]))
205
206 int
207 main(void)
208 {
209    /* Stdin should just contain chunk names, one per line */
210    png_uint_32 best_mask;
211    unsigned int best_c[4];
212    png_uint_32 chunks[CHUNK_COUNT_MAX];
213    png_byte known_chunk_count;
214
215    /* Not required; stop GCC whining: */
216    memset(best_c, 0xab, sizeof best_c);
217    best_mask = 0x12345678;
218
219    {
220       png_uint_32 this_chunk = 0;
221       png_byte n_chunks = 0;
222
223       for (;;)
224       {
225          int ch = getchar();
226
227          if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
228          {
229             if (this_chunk > 0xffffffU)
230                error("chunk name too long");
231
232             this_chunk = (this_chunk << 8) + (unsigned)ch;
233          }
234
235          else
236          {
237             if (this_chunk > 0)
238             {
239                if (this_chunk <= 0xffffffU)
240                   error("chunk name too short");
241
242                if (n_chunks >= CHUNK_COUNT_MAX)
243                   error("too many chunks (check CHUNK_COUNT_MAX)");
244
245                chunks[n_chunks++] = this_chunk;
246                this_chunk = 0;
247             }
248          }
249
250          if (ch < 0)
251             break;
252       }
253
254       /* 22 is the number of chunks currently defined (excluding fRAc), at any
255        * time it should also be in PNG_KNOWN_CHUNK_COUNT, but this run of
256        * chunkhash may be trying to add a chunk so allow bigger numbers.
257        */
258 #     ifdef PNG_KNOWN_CHUNK_COUNT
259 #        define CHUNK_COUNT_MIN PNG_KNOWN_CHUNK_COUNT
260 #     else
261 #        define CHUNK_COUNT_MIN 26
262 #     endif
263       if (n_chunks < CHUNK_COUNT_MIN)
264          error("too few chunks (expecting at least 26)");
265
266       known_chunk_count = n_chunks;
267    }
268
269    /* Exhaustive search of the hash parameters - in fact this isn't very slow at
270     * all.
271     */
272    {
273       unsigned int i1, c_zero = 0, c_one = 0;
274
275       for (i1=0; i1<NPRIMES; ++i1)
276       {
277          unsigned int i2;
278          unsigned int c[4];
279
280          fprintf(stderr, "TEST: %u\n", primes[i1]);
281          c[0] = primes[i1];
282
283          for (i2=0; i2<NPRIMES; ++i2)
284          {
285             unsigned int i3;
286
287             c[1] = primes[i2];
288
289             for (i3=0; i3<NPRIMES; ++i3)
290             {
291                unsigned int i4;
292
293                c[2] = primes[i3];
294
295                for (i4=0; i4<NPRIMES; ++i4)
296                {
297                   unsigned int i;
298                   png_uint_32 hash_mask = 0xffU;
299                   png_uint_32 hashes[CHUNK_COUNT_MAX];
300
301                   c[3] = primes[i4];
302
303                   while (hash_mask > 0xfU)
304                   {
305                      for (i=0; i<known_chunk_count; ++i)
306                      {
307                         unsigned int j;
308                         png_uint_32 hash = test_hash(chunks[i], c, hash_mask);
309
310                         for (j=0; j<i; ++j) if (hashes[j] == hash) goto next_i4;
311
312                         hashes[i] = hash;
313                      }
314
315                      hash_mask >>= 1;
316                   }
317
318                next_i4:
319                   if (hash_mask < 0xffU)
320                   {
321                      /* This worked */
322                      unsigned int best, c0 = 0, c1 = 0;
323
324                      hash_mask <<= 1;
325                      hash_mask += 1;
326
327                      for (i=0; i<4; ++i)
328                      {
329                         if (c[i] == 0)
330                            ++c0;
331
332                         else if (c[i] == 1)
333                            ++c1;
334                      }
335
336                      if (hash_mask == best_mask)
337                         best = (c0 > c_zero) || (c0 == c_zero && c1 > c_one);
338
339                      else
340                         best = (hash_mask < best_mask);
341
342                      if (best)
343                      {
344                         fprintf(stderr, "{%u,%u,%u,%u} & 0x%lx\n",
345                            c[0], c[1], c[2], c[3],
346                            (unsigned long)hash_mask);
347
348                         c_zero = c0;
349                         c_one = c1;
350                         best_mask = hash_mask;
351                         memcpy(best_c, c, sizeof best_c);
352                      }
353                   }
354                } /* i4 */
355             } /* i3 */
356          } /* i2 */
357       } /* i1 */
358    }
359
360    /* Calculate the LUT (png_chunk_hash) */
361    {
362       png_byte b;
363       png_byte lut[256];
364
365       /* A failure returns PNG_KNOWN_CHUNK_COUNT: */
366       memset(lut, known_chunk_count, sizeof lut);
367
368       for (b=0; b<known_chunk_count; ++b)
369          lut[test_hash(chunks[b], best_c, best_mask)] = b;
370
371       /* Validate the pngpriv.h hash function. */
372 #     define png_chunk_hash lut
373          {
374             unsigned int i;
375
376             for (i=0; i<known_chunk_count; ++i)
377             {
378                png_uint_32 chunk = chunks[i];
379
380                if (PNG_CHUNK_HASH(chunk) != i)
381                   error("internal error: hash didn't work!");
382             }
383          }
384 #     undef lut
385
386       /* Print all the results, first the stuff for pngpriv.h */
387       {
388          unsigned int i = 0;
389
390          while (strings[i] != 0)
391             puts(strings[i++]);
392
393          printf("#define PNG_CHUNK_HASH_MASK 0x%lxU\n",
394             (unsigned long)best_mask);
395          printf("#define PNG_CHUNK_HASH_C0 %u\n", best_c[0]);
396          printf("#define PNG_CHUNK_HASH_C1 %u\n", best_c[1]);
397          printf("#define PNG_CHUNK_HASH_C2 %u\n", best_c[2]);
398          printf("#define PNG_CHUNK_HASH_C3 %u\n", best_c[3]);
399
400          /* Print the hash codes of all the chunks */
401          putchar('\n');
402          print_chunks(chunks, known_chunk_count, 0, 0xffffffff, best_c,
403             best_mask, lut);
404          putchar('\n');
405          printf("#define PNG_KNOWN_CHUNK_COUNT %u\n", known_chunk_count);
406
407          while (strings[++i] != 0)
408             puts(strings[i]);
409
410          /* Now print the LUT */
411          fputs("   ", stdout);
412          {
413             unsigned int j;
414
415             for (j=0; j<=best_mask; ++j)
416             {
417                printf("%d", lut[j]);
418
419                if ((j % 16) == 15 && j < best_mask)
420                   printf(",\n   ");
421
422                else if (j < best_mask)
423                   printf(", ");
424
425                else
426                   printf("\n");
427             }
428          }
429
430          /* And the trailing text */
431          while (strings[++i] != 0)
432             puts(strings[i]);
433       }
434    }
435
436    exit(0);
437 }