]> mj.ucw.cz Git - libucw.git/blob - lib/hashfunc.c
Fixed a nasty bug in hash table allocation. HASH_AUTO_POOL must use
[libucw.git] / lib / hashfunc.c
1 /*
2  *      UCW Library -- Hyper-super-meta-alt-control-shift extra fast
3  *      str_len() and hash_*() routines
4  *
5  *      It is always at least as fast as the classical strlen() routine and for
6  *      strings longer than 100 characters, it is substantially faster.
7  *
8  *      (c) 2002, Robert Spalek <robert@ucw.cz>
9  *
10  *      This software may be freely distributed and used according to the terms
11  *      of the GNU Lesser General Public License.
12  */
13
14 #include "lib/lib.h"
15 #include "lib/hashfunc.h"
16 #include "lib/chartype.h"
17
18 /* The number of bits the hash in the function hash_*() is rotated by after
19  * every pass.  It should be prime with the word size.  */
20 #define SHIFT_BITS      7
21
22 /* A bit-mask which clears higher bytes than a given threshold.  */
23 static uns mask_higher_bits[sizeof(uns)];
24
25 static void CONSTRUCTOR
26 hashfunc_init(void)
27 {
28         uns i, j;
29         byte *str;
30         for (i=0; i<sizeof(uns); i++)
31         {
32                 str = (byte *) (mask_higher_bits + i);
33                 for (j=0; j<i; j++)
34                         str[j] = -1;
35                 for (j=i; j<sizeof(uns); j++)
36                         str[j] = 0;
37         }
38 }
39
40 static inline uns CONST
41 str_len_uns(uns x)
42 {
43         const uns sub = ~0U / 0xff;
44         const uns and = sub * 0x80;
45         uns a, i;
46         byte *bytes;
47         a = ~x & (x - sub) & and;
48         /* 
49          * x_2 = x - 0x01010101;
50          * x_3 = ~x & x_2;
51          * a = x_3 & 0x80808080;
52          *
53          * If all bytes of x are nonzero, then the highest bit of each byte of
54          * x_2 is lower or equal to the corresponding bit of x.  Hence x_3 has
55          * all these highest bits cleared (the target bit is set iff the source
56          * bit has changed from 0 to 1).  If a == 0, then we are sure there is
57          * no zero byte in x.
58          */
59         if (!a)
60                 return sizeof(uns);
61         bytes = (byte *) &x;
62         for (i=0; i<sizeof(uns) && bytes[i]; i++);
63         return i;
64 }
65
66 inline uns
67 str_len_aligned(const byte *str)
68 {
69         const uns *u = (const uns *) str;
70         uns len = 0;
71         while (1)
72         {
73                 uns l = str_len_uns(*u++);
74                 len += l;
75                 if (l < sizeof(uns))
76                         return len;
77         }
78 }
79
80 inline uns
81 hash_string_aligned(const byte *str)
82 {
83         const uns *u = (const uns *) str;
84         uns hash = 0;
85         while (1)
86         {
87                 uns last_len = str_len_uns(*u);
88                 hash = ROL(hash, SHIFT_BITS);
89                 if (last_len < sizeof(uns))
90                 {
91                         uns tmp = *u & mask_higher_bits[last_len];
92                         hash ^= tmp;
93                         return hash;
94                 }
95                 hash ^= *u++;
96         }
97 }
98
99 inline uns
100 hash_block_aligned(const byte *str, uns len)
101 {
102         const uns *u = (const uns *) str;
103         uns hash = 0;
104         while (len >= sizeof(uns))
105         {
106                 hash = ROL(hash, SHIFT_BITS) ^ *u++;
107                 len -= sizeof(uns);
108         }
109         hash = ROL(hash, SHIFT_BITS) ^ (*u & mask_higher_bits[len]);
110         return hash;
111 }
112
113 #ifndef CPU_ALLOW_UNALIGNED
114 uns
115 str_len(const byte *str)
116 {
117         uns shift = UNALIGNED_PART(str, uns);
118         if (!shift)
119                 return str_len_aligned(str);
120         else
121         {
122                 uns i;
123                 shift = sizeof(uns) - shift;
124                 for (i=0; i<shift; i++)
125                         if (!str[i])
126                                 return i;
127                 return shift + str_len_aligned(str + shift);
128         }
129 }
130
131 uns
132 hash_string(const byte *str)
133 {
134         uns shift = UNALIGNED_PART(str, uns);
135         if (!shift)
136                 return hash_string_aligned(str);
137         else
138         {
139                 uns hash = 0;
140                 uns i;
141                 for (i=0; ; i++)
142                 {
143                         uns modulo = i % sizeof(uns);
144                         uns shift;
145 #ifdef  CPU_LITTLE_ENDIAN
146                         shift = modulo;
147 #else
148                         shift = sizeof(uns) - 1 - modulo;
149 #endif
150                         if (!modulo)
151                                 hash = ROL(hash, SHIFT_BITS);
152                         if (!str[i])
153                                 break;
154                         hash ^= str[i] << (shift * 8);
155                 }
156                 return hash;
157         }
158 }
159
160 uns
161 hash_block(const byte *str, uns len)
162 {
163         uns shift = UNALIGNED_PART(str, uns);
164         if (!shift)
165                 return hash_block_aligned(str, len);
166         else
167         {
168                 uns hash = 0;
169                 uns i;
170                 for (i=0; ; i++)
171                 {
172                         uns modulo = i % sizeof(uns);
173                         uns shift;
174 #ifdef  CPU_LITTLE_ENDIAN
175                         shift = modulo;
176 #else
177                         shift = sizeof(uns) - 1 - modulo;
178 #endif
179                         if (!modulo)
180                                 hash = ROL(hash, SHIFT_BITS);
181                         if (i >= len)
182                                 break;
183                         hash ^= str[i] << (shift * 8);
184                 }
185                 return hash;
186         }
187 }
188 #endif
189
190 uns
191 hash_string_nocase(const byte *str)
192 {
193         uns hash = 0;
194         uns i;
195         for (i=0; ; i++)
196         {
197                 uns modulo = i % sizeof(uns);
198                 uns shift;
199 #ifdef  CPU_LITTLE_ENDIAN
200                 shift = modulo;
201 #else
202                 shift = sizeof(uns) - 1 - modulo;
203 #endif
204                 if (!modulo)
205                         hash = ROL(hash, SHIFT_BITS);
206                 if (!str[i])
207                         break;
208                 hash ^= Cupcase(str[i]) << (shift * 8);
209         }
210         return hash;
211 }