2 * quickhash - quickly hash a calc value using a quasi Fowler/Noll/Vo hash
4 * Copyright (C) 1999-2007 Landon Curt Noll
6 * Calc is open software; you can redistribute it and/or modify it under
7 * the terms of the version 2.1 of the GNU Lesser General Public License
8 * as published by the Free Software Foundation.
10 * Calc is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
13 * Public License for more details.
15 * A copy of version 2.1 of the GNU Lesser General Public License is
16 * distributed with calc under the filename COPYING-LGPL. You should have
17 * received a copy with calc; if not, write to Free Software Foundation, Inc.
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * @(#) $Revision: 30.1 $
21 * @(#) $Id: quickhash.c,v 30.1 2007/03/16 11:09:46 chongo Exp $
22 * @(#) $Source: /usr/local/src/bin/calc/RCS/quickhash.c,v $
24 * Under source code control: 1995/03/04 11:34:23
25 * File existed as early as: 1995
27 * chongo <was here> /\oo/\ http://www.isthe.com/chongo/
28 * Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
32 * NOTE: This file does not contain a hash interface. It is used by
33 * associative arrays and other internal processes.
42 * forward declarations
44 S_FUNC QCKHASH
assochash(ASSOC
*ap
, QCKHASH val
);
45 S_FUNC QCKHASH
listhash(LIST
*lp
, QCKHASH val
);
46 S_FUNC QCKHASH
mathash(MATRIX
*m
, QCKHASH val
);
47 S_FUNC QCKHASH
objhash(OBJECT
*op
, QCKHASH val
);
48 S_FUNC QCKHASH
randhash(RAND
*r
, QCKHASH val
);
49 S_FUNC QCKHASH
randomhash(RANDOM
*state
, QCKHASH val
);
50 S_FUNC QCKHASH
config_hash(CONFIG
*cfg
, QCKHASH val
);
51 S_FUNC QCKHASH
fnv_strhash(char *ch
, QCKHASH val
);
52 S_FUNC QCKHASH
fnv_STRhash(STRING
*str
, QCKHASH val
);
53 S_FUNC QCKHASH
fnv_fullhash(FULL
*v
, LEN len
, QCKHASH val
);
54 S_FUNC QCKHASH
fnv_zhash(ZVALUE z
, QCKHASH val
);
55 S_FUNC QCKHASH
hash_hash(HASH
*hash
, QCKHASH val
);
56 S_FUNC QCKHASH
blk_hash(BLOCK
*blk
, QCKHASH val
);
60 * quasi_fnv - quasi Fowler/Noll/Vo-0 32 bit hash
62 * The basis of this hash algorithm was taken from an idea sent
63 * as reviewer comments to the IEEE POSIX P1003.2 committee by:
65 * Phong Vo (http://www.research.att.com/info/kpv/)
66 * Glenn Fowler (http://www.research.att.com/~gsf/)
68 * In a subsequent ballot round:
70 * Landon Curt Noll (http://www.isthe.com/chongo/)
72 * improved on their algorithm. Some people tried this hash
73 * and found that it worked rather well. In an EMail message
74 * to Landon, they named it ``Fowler/Noll/Vo'' or the FNV hash.
76 * FNV hashes are architected to be fast while maintaining a low
77 * collision rate. The FNV speed allows one to quickly hash lots
78 * of data while maintaining a reasonable collision rate. See:
80 * http://www.isthe.com/chongo/tech/comp/fnv/index.html
82 * for more details as well as other forms of the FNV hash.
85 * x the value to hash (must not be longer than 32 bits)
86 * val previous QCKHASH value
89 * the next 32 bit QCKHASH
97 * NOTE: The (x) argument may be an expression such as something with
98 * a ++ or --. The macro must only use (x) once.
100 * NOTE: The (val) argument just be a lvalue / something to which
101 * a value can be assigned.
103 * The careful observer will note that (x) need not be a simple
104 * octet. This is not a bug, but a feature. The FNV hash was
105 * designed to operate on octets, not abstract objects such
106 * as associations, file descriptors and PRNG states.
107 * You will also notice that we sometimes add values directly
108 * to the (val) hash state. This is a simulation of hashing
111 * The Fowler/Noll/Vo hash does a very good job in producing
112 * a 32 bit hash arrays of octets in a short amount of time.
113 * It is not bad for hashing calc data as well. So doing a
114 * quick and dirty job of hashing on a part of a calc value
115 * is all that calc really needs.
117 * The core of the of the FNV hash has been adopted as the calc
118 * quick hash with the provision that it operates on 32 bit
119 * objects instead of octets. For calc's internal purposes,
120 * this is sufficent. For general FNV hashing, this is not
123 * It has been observed that gcc, when using -O, -O2, -O3 or
124 * better on an AMD or AMD-like processor (such as i686) will
125 * produce slightly faster code when using the shift/add
126 * expression than when performing a multiply with a constant.
128 #if defined(NO_HASH_CPU_OPTIMIZATION)
129 #define quasi_fnv(x,val) \
130 ((val) = (((QCKHASH)(val)*(QCKHASH)16777619) ^ ((QCKHASH)(x))))
132 #define quasi_fnv(x,val) ( \
133 ((val) += (((QCKHASH)(val)<<1) + ((QCKHASH)(val)<<4) + \
134 ((QCKHASH)(val)<<7) + ((QCKHASH)(val)<<8) + \
135 ((QCKHASH)(val)<<24))), \
136 ((val) ^= (QCKHASH)(x)) \
142 * fnv_qhash - compute the next Fowler/Noll/Vo hash given a NUMBER
145 * q pointer to a NUMBER
146 * val previous QCKHASH value
149 * the next 32 bit QCKHASH
151 #define fnv_qhash(q,val) \
152 (qisint(q) ? fnv_zhash((q)->num, (val)) : \
153 fnv_zhash((q)->num, fnv_zhash((q)->den, (val))))
157 * fnv_chash - compute the next Fowler/Noll/Vo hash given a COMPLEX
160 * c pointer to a COMPLEX
161 * val previous QCKHASH value
164 * the next 32 bit QCKHASH
166 #define fnv_chash(c,val) \
167 (cisreal(c) ? fnv_qhash((c)->real, (val)) : \
168 fnv_qhash((c)->real, fnv_qhash((c)->imag, (val))))
172 * hashvalue - calculate a hash value for a value.
174 * The hash does not have to be a perfect one, it is only used for
175 * making associations faster.
178 * vp pointer to a VALUE
179 * val previous QCKHASH value
185 hashvalue(VALUE
*vp
, QCKHASH val
)
187 switch (vp
->v_type
) {
190 return quasi_fnv(vp
->v_int
, val
);
192 return fnv_qhash(vp
->v_num
, val
);
194 return fnv_chash(vp
->v_com
, val
);
196 return fnv_STRhash(vp
->v_str
, val
);
200 return objhash(vp
->v_obj
, val
);
202 return listhash(vp
->v_list
, val
);
204 return assochash(vp
->v_assoc
, val
);
206 return mathash(vp
->v_mat
, val
);
209 return quasi_fnv(vp
->v_file
, val
);
211 return randhash(vp
->v_rand
, val
);
213 return randomhash(vp
->v_random
, val
);
215 return config_hash(vp
->v_config
, val
);
217 return hash_hash(vp
->v_hash
, val
);
219 return blk_hash(vp
->v_block
, val
);
222 return quasi_fnv((int)*vp
->v_octet
, val
);
224 return blk_hash(vp
->v_nblock
->blk
, val
);
226 math_error("Hashing unknown value");
234 * Return a trivial hash value for an association.
237 assochash(ASSOC
*ap
, QCKHASH val
)
240 * XXX - maybe we should hash the first few and last few values???
241 * Perhaps we should hash associations in a different but
245 return quasi_fnv(ap
->a_count
, val
);
250 * Return a trivial hash value for a list.
253 listhash(LIST
*lp
, QCKHASH val
)
258 switch (lp
->l_count
) {
260 /* empty list hashes to just V_LIST */
263 /* single element list hashes just that element */
264 return hashvalue(&lp
->l_first
->e_value
, V_LIST
+val
);
268 * multi element list hashes the first and last elements
270 return hashvalue(&lp
->l_first
->e_value
,
271 hashvalue(&lp
->l_last
->e_value
, V_LIST
+val
));
276 * Return a trivial hash value for a matrix.
279 mathash(MATRIX
*m
, QCKHASH val
)
286 * hash size parts of the matrix
289 quasi_fnv(m
->m_dim
, val
);
290 quasi_fnv(m
->m_size
, val
);
293 * hash the matrix index bounds
295 for (i
= m
->m_dim
- 1; i
>= 0; i
--) {
296 quasi_fnv(m
->m_min
[i
], val
);
297 quasi_fnv(m
->m_max
[i
], val
);
301 * hash the first 16 elements
304 for (i
= 0; ((i
< m
->m_size
) && (i
< 16)); i
++) {
305 val
= hashvalue(vp
++, val
);
309 * hash 10 more elements if they exist
313 vp
= (VALUE
*)&m
->m_table
[i
];
314 skip
= (m
->m_size
/ 11) + 1;
315 while (i
< m
->m_size
) {
316 val
= hashvalue(vp
, val
);
326 * Return a trivial hash value for an object.
329 objhash(OBJECT
*op
, QCKHASH val
)
333 quasi_fnv(op
->o_actions
->oa_index
, val
);
335 i
= op
->o_actions
->oa_count
;
337 val
= hashvalue(&op
->o_table
[i
], val
);
343 * randhash - return a trivial hash for an s100 state
346 * state - state to hash
349 * trivial hash integer
352 randhash(RAND
*r
, QCKHASH val
)
355 * hash the RAND state
358 /* unseeded state hashes to V_RAND */
361 /* hash control values */
363 quasi_fnv(r
->j
, val
);
364 quasi_fnv(r
->k
, val
);
365 quasi_fnv(r
->bits
, val
);
366 quasi_fnv(r
->need_to_skip
, val
);
368 /* hash the state arrays */
369 return fnv_fullhash(&r
->buffer
[0], SLEN
+SCNT
+SHUFLEN
, val
);
375 * randomhash - return a trivial hash for a Blum state
378 * state - state to hash
381 * trivial hash integer
384 randomhash(RANDOM
*state
, QCKHASH val
)
387 * unseeded RANDOM state hashes to V_RANDOM
389 if (!state
->seeded
) {
394 * hash a seeded RANDOM state
397 quasi_fnv(state
->buffer
+state
->bits
, val
);
398 if (state
->r
.v
!= NULL
) {
399 val
= fnv_zhash(state
->r
, val
);
401 if (state
->n
.v
!= NULL
) {
402 val
= fnv_zhash(state
->n
, val
);
409 * config_hash - return a trivial hash for a configuration state
412 config_hash(CONFIG
*cfg
, QCKHASH val
)
414 USB32 value
; /* value to hash from hash elements */
417 * build up a scalar value
419 * We will rotate a value left 5 bits and xor in each scalar element
421 value
= cfg
->outmode
;
422 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->outmode
);
423 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->outmode2
);
424 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->outdigits
);
425 /* epsilon is handeled out of order */
426 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->epsilonprec
);
427 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->traceflags
);
428 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->maxprint
);
429 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->mul2
);
430 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->sq2
);
431 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->pow2
);
432 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->redc2
);
433 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->tilde_ok
);
434 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->tab_ok
);
435 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->quomod
);
436 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->quo
);
437 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->mod
);
438 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->sqrt
);
439 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->appr
);
440 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->cfappr
);
441 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->cfsim
);
442 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->outround
);
443 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->round
);
444 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->leadzero
);
445 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->fullzero
);
446 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->maxscancount
);
447 /* prompt1 is handeled out of order */
448 /* prompt2 is handeled out of order */
449 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->blkmaxprint
);
450 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->blkverbose
);
451 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->blkbase
);
452 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->blkfmt
);
453 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->calc_debug
);
454 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->resource_debug
);
455 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->user_debug
);
456 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->verbose_quit
);
457 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->ctrl_d
);
458 /* program is handeled out of order */
459 /* basename is handeled out of order */
460 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->windows
);
461 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->cygwin
);
462 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->compile_custom
);
463 if (cfg
->allow_custom
!= NULL
&& *(cfg
->allow_custom
)) {
464 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)TRUE
);
466 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)FALSE
);
468 /* version is handeled out of order */
471 * hash the built up scalar
474 quasi_fnv(value
, val
);
477 * hash the strings and pointers if possible
480 val
= fnv_strhash(cfg
->prompt1
, val
);
483 val
= fnv_strhash(cfg
->prompt2
, val
);
486 val
= fnv_strhash(cfg
->program
, val
);
488 if (cfg
->base_name
) {
489 val
= fnv_strhash(cfg
->base_name
, val
);
492 val
= fnv_strhash(cfg
->version
, val
);
494 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->baseb
);
495 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->redecl_warn
);
496 value
= (((value
>>5) | (value
<<27)) ^ (USB32
)cfg
->dupvar_warn
);
499 * hash the epsilon if possible
502 val
= fnv_qhash(cfg
->epsilon
, val
);
509 * fnv_strhash - Fowler/Noll/Vo 32 bit hash of a null-terminated string
512 * ch the start of the string to hash
513 * val initial hash value
516 * a 32 bit QCKHASH value
519 fnv_strhash(char *ch
, QCKHASH val
)
522 * hash each character in the string
525 quasi_fnv(*ch
++, val
);
531 * fnv_STRhash - Fowler/Noll/Vo 32 bit hash of a STRING
534 * str the string to hash
535 * val initial hash value
538 * a 32 bit QCKHASH value
541 fnv_STRhash(STRING
*str
, QCKHASH val
)
550 * hash each character in the string
553 quasi_fnv(*ch
++, val
);
560 * fnv_fullhash - Fowler/Noll/Vo 32 bit hash of an array of HALFs
563 * v an array of FULLs
564 * len length of buffer FULLs
565 * val initial hash value
568 * a 32 bit QCKHASH value
571 fnv_fullhash(FULL
*v
, LEN len
, QCKHASH val
)
574 * hash each character in the string
577 quasi_fnv(*v
++, val
);
584 * fnv_zhash - Fowler/Noll/Vo 32 bit hash of ZVALUE
588 * val initial hash value
591 * a 32 bit QCKHASH value
594 fnv_zhash(ZVALUE z
, QCKHASH val
)
606 quasi_fnv(z
.sign
, val
);
614 f
|= (FULL
) *hp
++ << BASEB
;
632 * hash_hash - Fowler/Noll/Vo 32 bit hash of a block
635 * hash the HASH to quickhash
636 * val initial hash value
639 * a 32 bit QCKHASH value
642 hash_hash(HASH
*hash
, QCKHASH val
)
647 * hash each USB8 in the BLOCK
649 for (i
=0; i
< hash
->unionsize
; ++i
) {
650 quasi_fnv(hash
->h_union
.data
[i
], val
);
657 * blk_hash - Fowler/Noll/Vo 32 bit hash of a block
660 * blk the BLOCK to hash
661 * val initial hash value
664 * a 32 bit QCKHASH value
667 blk_hash(BLOCK
*blk
, QCKHASH val
)
671 if (blk
== NULL
) /* block has no data */
675 * hash each USB8 in the BLOCK
677 if (blk
->datalen
> 0) {
678 for (i
=0; i
< blk
->datalen
; ++i
) {
679 quasi_fnv(blk
->data
[i
], val
);