fnv64.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. /*
  2. * fnv_64 - 64 bit Fowler/Noll/Vo hash of a buffer or string
  3. *
  4. * @(#) $Revision: 5.5 $
  5. * @(#) $Id: fnv64.c,v 5.5 2012/03/21 01:38:12 chongo Exp $
  6. * @(#) $Source: /usr/local/src/cmd/fnv/RCS/fnv64.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 64 /* 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 64)\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_fnv64 - test the FNV64 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_fnv64(enum fnv_type hash_type, Fnv64_t init_hval,
  96. Fnv64_t mask, int v_flag, int code)
  97. {
  98. struct test_vector *t; /* FNV test vestor */
  99. Fnv64_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_64:
  107. printf("struct fnv0_64_test_vector fnv0_64_vector[] = {\n");
  108. break;
  109. case FNV1_64:
  110. printf("struct fnv1_64_test_vector fnv1_64_vector[] = {\n");
  111. break;
  112. case FNV1a_64:
  113. printf("struct fnv1a_64_test_vector fnv1a_64_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_64:
  130. case FNV1_64:
  131. hval = fnv_64_buf(t->buf, t->len, hval);
  132. break;
  133. case FNV1a_64:
  134. hval = fnv_64a_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. #if defined(HAVE_64BIT_LONG_LONG)
  144. /*
  145. * HAVE_64BIT_LONG_LONG testing
  146. */
  147. switch (code) {
  148. case 0: /* generate the test vector */
  149. printf(" { &fnv_test_str[%d], (Fnv64_t) 0x%016llxULL },\n",
  150. tstnum-1, hval & mask);
  151. break;
  152. case 1: /* validate against test vector */
  153. switch (hash_type) {
  154. case FNV0_64:
  155. if ((hval&mask) != (fnv0_64_vector[tstnum-1].fnv0_64 & mask)) {
  156. if (v_flag) {
  157. fprintf(stderr, "%s: failed fnv0_64 test # %d\n",
  158. program, tstnum);
  159. fprintf(stderr, "%s: test # 1 is 1st test\n", program);
  160. fprintf(stderr,
  161. "%s: expected 0x%016llx != generated: 0x%016llx\n",
  162. program,
  163. (hval&mask),
  164. (fnv0_64_vector[tstnum-1].fnv0_64 & mask));
  165. }
  166. return tstnum;
  167. }
  168. break;
  169. case FNV1_64:
  170. if ((hval&mask) != (fnv1_64_vector[tstnum-1].fnv1_64 & mask)) {
  171. if (v_flag) {
  172. fprintf(stderr, "%s: failed fnv1_64 test # %d\n",
  173. program, tstnum);
  174. fprintf(stderr, "%s: test # 1 is 1st test\n", program);
  175. fprintf(stderr,
  176. "%s: expected 0x%016llx != generated: 0x%016llx\n",
  177. program,
  178. (hval&mask),
  179. (fnv1_64_vector[tstnum-1].fnv1_64 & mask));
  180. }
  181. return tstnum;
  182. }
  183. break;
  184. case FNV1a_64:
  185. if ((hval&mask) != (fnv1a_64_vector[tstnum-1].fnv1a_64 &mask)) {
  186. if (v_flag) {
  187. fprintf(stderr, "%s: failed fnv1a_64 test # %d\n",
  188. program, tstnum);
  189. fprintf(stderr, "%s: test # 1 is 1st test\n", program);
  190. fprintf(stderr,
  191. "%s: expected 0x%016llx != generated: 0x%016llx\n",
  192. program,
  193. (hval&mask),
  194. (fnv1a_64_vector[tstnum-1].fnv1a_64 & mask));
  195. }
  196. return tstnum;
  197. }
  198. break;
  199. }
  200. break;
  201. default:
  202. fprintf(stderr, "%s: -m %d not implemented yet\n", program, code);
  203. exit(14);
  204. }
  205. #else /* HAVE_64BIT_LONG_LONG */
  206. /*
  207. * non HAVE_64BIT_LONG_LONG testing
  208. */
  209. switch (code) {
  210. case 0: /* generate the test vector */
  211. printf(" { &fnv_test_str[%d], "
  212. "(Fnv64_t) {0x%08lxUL, 0x%08lxUL} },\n",
  213. tstnum-1,
  214. (hval.w32[0] & mask.w32[0]),
  215. (hval.w32[1] & mask.w32[1]));
  216. break;
  217. case 1: /* validate against test vector */
  218. switch (hash_type) {
  219. case FNV0_64:
  220. if (((hval.w32[0] & mask.w32[0]) !=
  221. (fnv0_64_vector[tstnum-1].fnv0_64.w32[0] &
  222. mask.w32[0])) &&
  223. ((hval.w32[1] & mask.w32[1]) !=
  224. (fnv0_64_vector[tstnum-1].fnv0_64.w32[1] &
  225. mask.w32[1]))) {
  226. if (v_flag) {
  227. fprintf(stderr, "%s: failed fnv0_64 test # %d\n",
  228. program, tstnum);
  229. fprintf(stderr, "%s: test # 1 is 1st test\n", program);
  230. fprintf(stderr,
  231. "%s: expected 0x%08llx%08llx != "
  232. "generated: 0x%08llx%08llx\n",
  233. program,
  234. (hval.w32[0] & mask.w32[0]),
  235. (hval.w32[1] & mask.w32[1]),
  236. ((fnv0_64_vector[tstnum-1].fnv0_64.w32[0] &
  237. mask.w32[0])),
  238. ((fnv0_64_vector[tstnum-1].fnv0_64.w32[1] &
  239. mask.w32[1])));
  240. }
  241. return tstnum;
  242. }
  243. break;
  244. case FNV1_64:
  245. if (((hval.w32[0] & mask.w32[0]) !=
  246. (fnv1_64_vector[tstnum-1].fnv1_64.w32[0] &
  247. mask.w32[0])) &&
  248. ((hval.w32[1] & mask.w32[1]) !=
  249. (fnv1_64_vector[tstnum-1].fnv1_64.w32[1] &
  250. mask.w32[1]))) {
  251. if (v_flag) {
  252. fprintf(stderr, "%s: failed fnv1_64 test # %d\n",
  253. program, tstnum);
  254. fprintf(stderr, "%s: test # 1 is 1st test\n", program);
  255. fprintf(stderr,
  256. "%s: expected 0x%08llx%08llx != "
  257. "generated: 0x%08llx%08llx\n",
  258. program,
  259. (hval.w32[0] & mask.w32[0]),
  260. (hval.w32[1] & mask.w32[1]),
  261. ((fnv1_64_vector[tstnum-1].fnv1_64.w32[0] &
  262. mask.w32[0])),
  263. ((fnv1_64_vector[tstnum-1].fnv1_64.w32[1] &
  264. mask.w32[1])));
  265. }
  266. return tstnum;
  267. }
  268. break;
  269. case FNV1a_64:
  270. if (((hval.w32[0] & mask.w32[0]) !=
  271. (fnv1a_64_vector[tstnum-1].fnv1a_64.w32[0] &
  272. mask.w32[0])) &&
  273. ((hval.w32[1] & mask.w32[1]) !=
  274. (fnv1a_64_vector[tstnum-1].fnv1a_64.w32[1] &
  275. mask.w32[1]))) {
  276. if (v_flag) {
  277. fprintf(stderr, "%s: failed fnv1a_64 test # %d\n",
  278. program, tstnum);
  279. fprintf(stderr, "%s: test # 1 is 1st test\n", program);
  280. fprintf(stderr,
  281. "%s: expected 0x%08llx%08llx != "
  282. "generated: 0x%08llx%08llx\n",
  283. program,
  284. (hval.w32[0] & mask.w32[0]),
  285. (hval.w32[1] & mask.w32[1]),
  286. ((fnv1a_64_vector[tstnum-1].fnv1a_64.w32[0] &
  287. mask.w32[0])),
  288. ((fnv1a_64_vector[tstnum-1].fnv1a_64.w32[1] &
  289. mask.w32[1])));
  290. }
  291. return tstnum;
  292. }
  293. break;
  294. }
  295. break;
  296. default:
  297. fprintf(stderr, "%s: -m %d not implemented yet\n", program, code);
  298. exit(15);
  299. }
  300. #endif /* HAVE_64BIT_LONG_LONG */
  301. }
  302. /*
  303. * print completion if generating test vectors
  304. */
  305. if (code == 0) {
  306. #if defined(HAVE_64BIT_LONG_LONG)
  307. printf(" { NULL, (Fnv64_t) 0 }\n");
  308. #else /* HAVE_64BIT_LONG_LONG */
  309. printf(" { NULL, (Fnv64_t) {0,0} }\n");
  310. #endif /* HAVE_64BIT_LONG_LONG */
  311. printf("};\n");
  312. }
  313. /*
  314. * no failures, return code 0 ==> all OK
  315. */
  316. return 0;
  317. }
  318. /*
  319. * main - the main function
  320. *
  321. * See the above usage for details.
  322. */
  323. int
  324. main(int argc, char *argv[])
  325. {
  326. char buf[BUF_SIZE+1]; /* read buffer */
  327. int readcnt; /* number of characters written */
  328. Fnv64_t hval; /* current hash value */
  329. int s_flag = 0; /* 1 => -s was given, hash args as strings */
  330. int m_flag = 0; /* 1 => print multiple hashes, one per arg */
  331. int v_flag = 0; /* 1 => verbose hash print */
  332. int b_flag = WIDTH; /* -b flag value */
  333. int t_flag = -1; /* FNV test vector code (0=>print, 1=>test) */
  334. enum fnv_type hash_type = FNV_NONE; /* type of FNV hash to perform */
  335. Fnv64_t bmask; /* mask to apply to output */
  336. extern char *optarg; /* option argument */
  337. extern int optind; /* argv index of the next arg */
  338. int fd; /* open file to process */
  339. char *p;
  340. int i;
  341. /*
  342. * parse args
  343. */
  344. program = argv[0];
  345. while ((i = getopt(argc, argv, "b:mst:v")) != -1) {
  346. switch (i) {
  347. case 'b': /* bcnt bit mask count */
  348. b_flag = atoi(optarg);
  349. break;
  350. case 'm': /* print multiple hashes, one per arg */
  351. m_flag = 1;
  352. break;
  353. case 's': /* hash args as strings */
  354. s_flag = 1;
  355. break;
  356. case 't': /* FNV test vector code */
  357. t_flag = atoi(optarg);
  358. if (t_flag < 0 || t_flag > 1) {
  359. fprintf(stderr, "%s: -t code must be 0 or 1\n", program);
  360. fprintf(stderr, usage, program, FNV_VERSION);
  361. exit(1);
  362. }
  363. m_flag = 1;
  364. break;
  365. case 'v': /* verbose hash print */
  366. m_flag = 1;
  367. v_flag = 1;
  368. break;
  369. default:
  370. fprintf(stderr, usage, program, FNV_VERSION);
  371. exit(1);
  372. }
  373. }
  374. /* -t code incompatible with -b, -m and args */
  375. if (t_flag >= 0) {
  376. if (b_flag != WIDTH) {
  377. fprintf(stderr, "%s: -t code incompatible with -b\n", program);
  378. exit(2);
  379. }
  380. if (s_flag != 0) {
  381. fprintf(stderr, "%s: -t code incompatible with -s\n", program);
  382. exit(3);
  383. }
  384. if (optind < argc) {
  385. fprintf(stderr, "%s: -t code incompatible args\n", program);
  386. exit(4);
  387. }
  388. }
  389. /* -s requires at least 1 arg */
  390. if (s_flag && optind >= argc) {
  391. fprintf(stderr, usage, program, FNV_VERSION);
  392. exit(5);
  393. }
  394. /* limit -b values */
  395. if (b_flag < 0 || b_flag > WIDTH) {
  396. fprintf(stderr, "%s: -b bcnt: %d must be >= 0 and < %d\n",
  397. program, b_flag, WIDTH);
  398. exit(6);
  399. }
  400. #if defined(HAVE_64BIT_LONG_LONG)
  401. if (b_flag == WIDTH) {
  402. bmask = (Fnv64_t)0xffffffffffffffffULL;
  403. } else {
  404. bmask = (Fnv64_t)((1ULL << b_flag) - 1ULL);
  405. }
  406. #else /* HAVE_64BIT_LONG_LONG */
  407. if (b_flag == WIDTH) {
  408. bmask.w32[0] = 0xffffffffUL;
  409. bmask.w32[1] = 0xffffffffUL;
  410. } else if (b_flag >= WIDTH/2) {
  411. bmask.w32[0] = 0xffffffffUL;
  412. bmask.w32[1] = ((1UL << (b_flag-(WIDTH/2))) - 1UL);
  413. } else {
  414. bmask.w32[0] = ((1UL << b_flag) - 1UL);
  415. bmask.w32[1] = 0UL;
  416. }
  417. #endif /* HAVE_64BIT_LONG_LONG */
  418. /*
  419. * start with the initial basis depending on the hash type
  420. */
  421. p = strrchr(program, '/');
  422. if (p == NULL) {
  423. p = program;
  424. } else {
  425. ++p;
  426. }
  427. if (strcmp(p, "fnv064") == 0 || strcmp(p, "no64bit_fnv064") == 0) {
  428. /* using non-recommended FNV-0 and zero initial basis */
  429. hval = FNV0_64_INIT;
  430. hash_type = FNV0_64;
  431. } else if (strcmp(p, "fnv164") == 0 || strcmp(p, "no64bit_fnv164") == 0) {
  432. /* using FNV-1 and non-zero initial basis */
  433. hval = FNV1_64_INIT;
  434. hash_type = FNV1_64;
  435. } else if (strcmp(p, "fnv1a64") == 0 || strcmp(p, "no64bit_fnv1a64") == 0) {
  436. /* start with the FNV-1a initial basis */
  437. hval = FNV1A_64_INIT;
  438. hash_type = FNV1a_64;
  439. } else {
  440. fprintf(stderr, "%s: unknown program name, unknown hash type\n",
  441. program);
  442. exit(7);
  443. }
  444. /*
  445. * FNV test vector processing, if needed
  446. */
  447. if (t_flag >= 0) {
  448. int code; /* test vector that failed, starting at 1 */
  449. /*
  450. * perform all tests
  451. */
  452. code = test_fnv64(hash_type, hval, bmask, v_flag, t_flag);
  453. /*
  454. * evaluate the tests
  455. */
  456. if (code == 0) {
  457. if (v_flag) {
  458. printf("passed\n");
  459. }
  460. exit(0);
  461. } else {
  462. printf("failed vector (1 is 1st test): %d\n", code);
  463. exit(8);
  464. }
  465. }
  466. /*
  467. * string hashing
  468. */
  469. if (s_flag) {
  470. /* hash any other strings */
  471. for (i=optind; i < argc; ++i) {
  472. switch (hash_type) {
  473. case FNV0_64:
  474. case FNV1_64:
  475. hval = fnv_64_str(argv[i], hval);
  476. break;
  477. case FNV1a_64:
  478. hval = fnv_64a_str(argv[i], hval);
  479. break;
  480. default:
  481. unknown_hash_type(program, hash_type, 9); /* exit(9) */
  482. /*NOTREACHED*/
  483. }
  484. if (m_flag) {
  485. print_fnv64(hval, bmask, v_flag, argv[i]);
  486. }
  487. }
  488. /*
  489. * file hashing
  490. */
  491. } else {
  492. /*
  493. * case: process only stdin
  494. */
  495. if (optind >= argc) {
  496. /* case: process only stdin */
  497. while ((readcnt = read(0, buf, BUF_SIZE)) > 0) {
  498. switch (hash_type) {
  499. case FNV0_64:
  500. case FNV1_64:
  501. hval = fnv_64_buf(buf, readcnt, hval);
  502. break;
  503. case FNV1a_64:
  504. hval = fnv_64a_buf(buf, readcnt, hval);
  505. break;
  506. default:
  507. unknown_hash_type(program, hash_type, 10); /* exit(10) */
  508. /*NOTREACHED*/
  509. }
  510. }
  511. if (m_flag) {
  512. print_fnv64(hval, bmask, v_flag, "(stdin)");
  513. }
  514. } else {
  515. /*
  516. * process any other files
  517. */
  518. for (i=optind; i < argc; ++i) {
  519. /* open the file */
  520. fd = open(argv[i], O_RDONLY);
  521. if (fd < 0) {
  522. fprintf(stderr, "%s: unable to open file: %s\n",
  523. program, argv[i]);
  524. exit(4);
  525. }
  526. /* hash the file */
  527. while ((readcnt = read(fd, buf, BUF_SIZE)) > 0) {
  528. switch (hash_type) {
  529. case FNV0_64:
  530. case FNV1_64:
  531. hval = fnv_64_buf(buf, readcnt, hval);
  532. break;
  533. case FNV1a_64:
  534. hval = fnv_64a_buf(buf, readcnt, hval);
  535. break;
  536. default:
  537. unknown_hash_type(program, hash_type, 11);/* exit(11) */
  538. /*NOTREACHED*/
  539. }
  540. }
  541. /* finish processing the file */
  542. if (m_flag) {
  543. print_fnv64(hval, bmask, v_flag, argv[i]);
  544. }
  545. close(fd);
  546. }
  547. }
  548. }
  549. /*
  550. * report hash and exit
  551. */
  552. if (!m_flag) {
  553. print_fnv64(hval, bmask, v_flag, "");
  554. }
  555. return 0; /* exit(0); */
  556. }