You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

467 lines
12 KiB

  1. /*
  2. * fnv32 - 32 bit Fowler/Noll/Vo hash of a buffer or string
  3. *
  4. * @(#) $Revision: 5.5 $
  5. * @(#) $Id: fnv32.c,v 5.5 2012/03/21 01:38:12 chongo Exp $
  6. * @(#) $Source: /usr/local/src/cmd/fnv/RCS/fnv32.c,v $
  7. *
  8. ***
  9. *
  10. * Fowler/Noll/Vo hash
  11. *
  12. * The basis of this hash algorithm was taken from an idea sent
  13. * as reviewer comments to the IEEE POSIX P1003.2 committee by:
  14. *
  15. * Phong Vo (http://www.research.att.com/info/kpv/)
  16. * Glenn Fowler (http://www.research.att.com/~gsf/)
  17. *
  18. * In a subsequent ballot round:
  19. *
  20. * Landon Curt Noll (http://www.isthe.com/chongo/)
  21. *
  22. * improved on their algorithm. Some people tried this hash
  23. * and found that it worked rather well. In an EMail message
  24. * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.
  25. *
  26. * FNV hashes are designed to be fast while maintaining a low
  27. * collision rate. The FNV speed allows one to quickly hash lots
  28. * of data while maintaining a reasonable collision rate. See:
  29. *
  30. * http://www.isthe.com/chongo/tech/comp/fnv/index.html
  31. *
  32. * for more details as well as other forms of the FNV hash.
  33. *
  34. ***
  35. *
  36. * Please do not copyright this code. This code is in the public domain.
  37. *
  38. * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  39. * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
  40. * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  41. * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  42. * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  43. * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  44. * PERFORMANCE OF THIS SOFTWARE.
  45. *
  46. * By:
  47. * chongo <Landon Curt Noll> /\oo/\
  48. * http://www.isthe.com/chongo/
  49. *
  50. * Share and Enjoy! :-)
  51. */
  52. #include <stdio.h>
  53. #include <unistd.h>
  54. #include <stdlib.h>
  55. #include <sys/types.h>
  56. #include <sys/stat.h>
  57. #include <fcntl.h>
  58. #include <string.h>
  59. #include "longlong.h"
  60. #include "fnv.h"
  61. #define WIDTH 32 /* bit width of hash */
  62. #define BUF_SIZE (32*1024) /* number of bytes to hash at a time */
  63. static char *usage =
  64. "usage: %s [-b bcnt] [-m] [-s arg] [-t code] [-v] [arg ...]\n"
  65. "\n"
  66. "\t-b bcnt\tmask off all but the lower bcnt bits (default 32)\n"
  67. "\t-m\tmultiple hashes, one per line for each arg\n"
  68. "\t-s\thash arg as a string (ignoring terminating NUL bytes)\n"
  69. "\t-t code\t test hash code: (0 ==> generate test vectors\n"
  70. "\t\t\t\t 1 ==> validate against FNV test vectors)\n"
  71. "\t-v\tverbose mode, print arg after hash (implies -m)\n"
  72. "\targ\tstring (if -s was given) or filename (default stdin)\n"
  73. "\n"
  74. "\tNOTE: Programs that begin with fnv0 implement the FNV-0 hash.\n"
  75. "\t The FNV-0 hash is historic FNV algorithm that is now deprecated.\n"
  76. "\n"
  77. "\tSee http://www.isthe.com/chongo/tech/comp/fnv/index.html for more info.\n"
  78. "\n"
  79. "\t@(#) FNV Version: %s\n";
  80. static char *program; /* our name */
  81. /*
  82. * test_fnv32 - test the FNV32 hash
  83. *
  84. * given:
  85. * hash_type type of FNV hash to test
  86. * init_hval initial hash value
  87. * mask lower bit mask
  88. * v_flag 1 => print test failure info on stderr
  89. * code 0 ==> generate FNV test vectors
  90. * 1 ==> validate against FNV test vectors
  91. *
  92. * returns: 0 ==> OK, else test vector failure number
  93. */
  94. static int
  95. test_fnv32(enum fnv_type hash_type, Fnv32_t init_hval,
  96. Fnv32_t mask, int v_flag, int code)
  97. {
  98. struct test_vector *t; /* FNV test vestor */
  99. Fnv32_t hval; /* current hash value */
  100. int tstnum; /* test vector that failed, starting at 1 */
  101. /*
  102. * print preamble if generating test vectors
  103. */
  104. if (code == 0) {
  105. switch (hash_type) {
  106. case FNV0_32:
  107. printf("struct fnv0_32_test_vector fnv0_32_vector[] = {\n");
  108. break;
  109. case FNV1_32:
  110. printf("struct fnv1_32_test_vector fnv1_32_vector[] = {\n");
  111. break;
  112. case FNV1a_32:
  113. printf("struct fnv1a_32_test_vector fnv1a_32_vector[] = {\n");
  114. break;
  115. default:
  116. unknown_hash_type(program, hash_type, 12); /* exit(12) */
  117. /*NOTREACHED*/
  118. }
  119. }
  120. /*
  121. * loop thru all test vectors
  122. */
  123. for (t = fnv_test_str, tstnum = 1; t->buf != NULL; ++t, ++tstnum) {
  124. /*
  125. * compute the FNV hash
  126. */
  127. hval = init_hval;
  128. switch (hash_type) {
  129. case FNV0_32:
  130. case FNV1_32:
  131. hval = fnv_32_buf(t->buf, t->len, hval);
  132. break;
  133. case FNV1a_32:
  134. hval = fnv_32a_buf(t->buf, t->len, hval);
  135. break;
  136. default:
  137. unknown_hash_type(program, hash_type, 13); /* exit(13) */
  138. /*NOTREACHED*/
  139. }
  140. /*
  141. * print the vector
  142. */
  143. switch (code) {
  144. case 0: /* generate the test vector */
  145. printf(" { &fnv_test_str[%d], (Fnv32_t) 0x%08lxUL },\n",
  146. tstnum-1, hval & mask);
  147. break;
  148. case 1: /* validate against test vector */
  149. switch (hash_type) {
  150. case FNV0_32:
  151. if ((hval&mask) != (fnv0_32_vector[tstnum-1].fnv0_32 & mask)) {
  152. if (v_flag) {
  153. fprintf(stderr, "%s: failed fnv0_32 test # %d\n",
  154. program, tstnum);
  155. fprintf(stderr, "%s: test # 1 is 1st test\n", program);
  156. fprintf(stderr,
  157. "%s: expected 0x%08lx != generated: 0x%08lx\n",
  158. program, (hval&mask),
  159. (fnv0_32_vector[tstnum-1].fnv0_32 & mask));
  160. }
  161. return tstnum;
  162. }
  163. break;
  164. case FNV1_32:
  165. if ((hval&mask) != (fnv1_32_vector[tstnum-1].fnv1_32 & mask)) {
  166. if (v_flag) {
  167. fprintf(stderr, "%s: failed fnv1_32 test # %d\n",
  168. program, tstnum);
  169. fprintf(stderr, "%s: test # 1 is 1st test\n", program);
  170. fprintf(stderr,
  171. "%s: expected 0x%08lx != generated: 0x%08lx\n",
  172. program, (hval&mask),
  173. (fnv1_32_vector[tstnum-1].fnv1_32 & mask));
  174. }
  175. return tstnum;
  176. }
  177. break;
  178. case FNV1a_32:
  179. if ((hval&mask) != (fnv1a_32_vector[tstnum-1].fnv1a_32 &mask)) {
  180. if (v_flag) {
  181. fprintf(stderr, "%s: failed fnv1a_32 test # %d\n",
  182. program, tstnum);
  183. fprintf(stderr, "%s: test # 1 is 1st test\n", program);
  184. fprintf(stderr,
  185. "%s: expected 0x%08lx != generated: 0x%08lx\n",
  186. program, (hval&mask),
  187. (fnv1a_32_vector[tstnum-1].fnv1a_32 & mask));
  188. }
  189. return tstnum;
  190. }
  191. break;
  192. }
  193. break;
  194. default:
  195. fprintf(stderr, "%s: -m %d not implemented yet\n", program, code);
  196. exit(14);
  197. }
  198. }
  199. /*
  200. * print completion if generating test vectors
  201. */
  202. if (code == 0) {
  203. printf(" { NULL, 0 }\n");
  204. printf("};\n");
  205. }
  206. /*
  207. * no failures, return code 0 ==> all OK
  208. */
  209. return 0;
  210. }
  211. /*
  212. * main - the main function
  213. *
  214. * See the above usage for details.
  215. */
  216. int
  217. main(int argc, char *argv[])
  218. {
  219. char buf[BUF_SIZE+1]; /* read buffer */
  220. int readcnt; /* number of characters written */
  221. Fnv32_t hval; /* current hash value */
  222. int s_flag = 0; /* 1 => -s was given, hash args as strings */
  223. int m_flag = 0; /* 1 => print multiple hashes, one per arg */
  224. int v_flag = 0; /* 1 => verbose hash print */
  225. int b_flag = WIDTH; /* -b flag value */
  226. int t_flag = -1; /* FNV test vector code (0=>print, 1=>test) */
  227. enum fnv_type hash_type = FNV_NONE; /* type of FNV hash to perform */
  228. Fnv32_t bmask; /* mask to apply to output */
  229. extern char *optarg; /* option argument */
  230. extern int optind; /* argv index of the next arg */
  231. int fd; /* open file to process */
  232. char *p;
  233. int i;
  234. /*
  235. * parse args
  236. */
  237. program = argv[0];
  238. while ((i = getopt(argc, argv, "b:mst:v")) != -1) {
  239. switch (i) {
  240. case 'b': /* bcnt bit mask count */
  241. b_flag = atoi(optarg);
  242. break;
  243. case 'm': /* print multiple hashes, one per arg */
  244. m_flag = 1;
  245. break;
  246. case 's': /* hash args as strings */
  247. s_flag = 1;
  248. break;
  249. case 't': /* FNV test vector code */
  250. t_flag = atoi(optarg);
  251. if (t_flag < 0 || t_flag > 1) {
  252. fprintf(stderr, "%s: -t code must be 0 or 1\n", program);
  253. fprintf(stderr, usage, program, FNV_VERSION);
  254. exit(1);
  255. }
  256. m_flag = 1;
  257. break;
  258. case 'v': /* verbose hash print */
  259. m_flag = 1;
  260. v_flag = 1;
  261. break;
  262. default:
  263. fprintf(stderr, usage, program, FNV_VERSION);
  264. exit(1);
  265. }
  266. }
  267. /* -t code incompatible with -b, -m and args */
  268. if (t_flag >= 0) {
  269. if (b_flag != WIDTH) {
  270. fprintf(stderr, "%s: -t code incompatible with -b\n", program);
  271. exit(2);
  272. }
  273. if (s_flag != 0) {
  274. fprintf(stderr, "%s: -t code incompatible with -s\n", program);
  275. exit(3);
  276. }
  277. if (optind < argc) {
  278. fprintf(stderr, "%s: -t code incompatible args\n", program);
  279. exit(4);
  280. }
  281. }
  282. /* -s requires at least 1 arg */
  283. if (s_flag && optind >= argc) {
  284. fprintf(stderr, usage, program, FNV_VERSION);
  285. exit(5);
  286. }
  287. /* limit -b values */
  288. if (b_flag < 0 || b_flag > WIDTH) {
  289. fprintf(stderr, "%s: -b bcnt: %d must be >= 0 and < %d\n",
  290. program, b_flag, WIDTH);
  291. exit(6);
  292. }
  293. if (b_flag == WIDTH) {
  294. bmask = (Fnv32_t)0xffffffff;
  295. } else {
  296. bmask = (Fnv32_t)((1 << b_flag) - 1);
  297. }
  298. /*
  299. * start with the initial basis depending on the hash type
  300. */
  301. p = strrchr(program, '/');
  302. if (p == NULL) {
  303. p = program;
  304. } else {
  305. ++p;
  306. }
  307. if (strcmp(p, "fnv032") == 0) {
  308. /* using non-recommended FNV-0 and zero initial basis */
  309. hval = FNV0_32_INIT;
  310. hash_type = FNV0_32;
  311. } else if (strcmp(p, "fnv132") == 0) {
  312. /* using FNV-1 and non-zero initial basis */
  313. hval = FNV1_32_INIT;
  314. hash_type = FNV1_32;
  315. } else if (strcmp(p, "fnv1a32") == 0) {
  316. /* start with the FNV-1a initial basis */
  317. hval = FNV1_32A_INIT;
  318. hash_type = FNV1a_32;
  319. } else {
  320. fprintf(stderr, "%s: unknown program name, unknown hash type\n",
  321. program);
  322. exit(7);
  323. }
  324. /*
  325. * FNV test vector processing, if needed
  326. */
  327. if (t_flag >= 0) {
  328. int code; /* test vector that failed, starting at 1 */
  329. /*
  330. * perform all tests
  331. */
  332. code = test_fnv32(hash_type, hval, bmask, v_flag, t_flag);
  333. /*
  334. * evaluate the tests
  335. */
  336. if (code == 0) {
  337. if (v_flag) {
  338. printf("passed\n");
  339. }
  340. exit(0);
  341. } else {
  342. printf("failed vector (1 is 1st test): %d\n", code);
  343. exit(8);
  344. }
  345. }
  346. /*
  347. * string hashing
  348. */
  349. if (s_flag) {
  350. /* hash any other strings */
  351. for (i=optind; i < argc; ++i) {
  352. switch (hash_type) {
  353. case FNV0_32:
  354. case FNV1_32:
  355. hval = fnv_32_str(argv[i], hval);
  356. break;
  357. case FNV1a_32:
  358. hval = fnv_32a_str(argv[i], hval);
  359. break;
  360. default:
  361. unknown_hash_type(program, hash_type, 9); /* exit(9) */
  362. /*NOTREACHED*/
  363. }
  364. if (m_flag) {
  365. print_fnv32(hval, bmask, v_flag, argv[i]);
  366. }
  367. }
  368. /*
  369. * file hashing
  370. */
  371. } else {
  372. /*
  373. * case: process only stdin
  374. */
  375. if (optind >= argc) {
  376. /* case: process only stdin */
  377. while ((readcnt = read(0, buf, BUF_SIZE)) > 0) {
  378. switch (hash_type) {
  379. case FNV0_32:
  380. case FNV1_32:
  381. hval = fnv_32_buf(buf, readcnt, hval);
  382. break;
  383. case FNV1a_32:
  384. hval = fnv_32a_buf(buf, readcnt, hval);
  385. break;
  386. default:
  387. unknown_hash_type(program, hash_type, 10); /* exit(10) */
  388. /*NOTREACHED*/
  389. }
  390. }
  391. if (m_flag) {
  392. print_fnv32(hval, bmask, v_flag, "(stdin)");
  393. }
  394. } else {
  395. /*
  396. * process any other files
  397. */
  398. for (i=optind; i < argc; ++i) {
  399. /* open the file */
  400. fd = open(argv[i], O_RDONLY);
  401. if (fd < 0) {
  402. fprintf(stderr, "%s: unable to open file: %s\n",
  403. program, argv[i]);
  404. exit(4);
  405. }
  406. /* hash the file */
  407. while ((readcnt = read(fd, buf, BUF_SIZE)) > 0) {
  408. switch (hash_type) {
  409. case FNV0_32:
  410. case FNV1_32:
  411. hval = fnv_32_buf(buf, readcnt, hval);
  412. break;
  413. case FNV1a_32:
  414. hval = fnv_32a_buf(buf, readcnt, hval);
  415. break;
  416. default:
  417. unknown_hash_type(program, hash_type, 11);/* exit(11) */
  418. /*NOTREACHED*/
  419. }
  420. }
  421. /* finish processing the file */
  422. if (m_flag) {
  423. print_fnv32(hval, bmask, v_flag, argv[i]);
  424. }
  425. close(fd);
  426. }
  427. }
  428. }
  429. /*
  430. * report hash and exit
  431. */
  432. if (!m_flag) {
  433. print_fnv32(hval, bmask, v_flag, "");
  434. }
  435. return 0; /* exit(0); */
  436. }