]> mj.ucw.cz Git - libucw.git/blob - ucw/hashfunc.c
Libucw: A test for mmap fastbufs.
[libucw.git] / ucw / 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 "ucw/lib.h"
15 #include "ucw/hashfunc.h"
16 #include "ucw/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 char *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 char *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 char *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 char *str)
133 {
134         const byte *s = str;
135         uns shift = UNALIGNED_PART(s, uns);
136         if (!shift)
137                 return hash_string_aligned(s);
138         else
139         {
140                 uns hash = 0;
141                 uns i;
142                 for (i=0; ; i++)
143                 {
144                         uns modulo = i % sizeof(uns);
145                         uns shift;
146 #ifdef  CPU_LITTLE_ENDIAN
147                         shift = modulo;
148 #else
149                         shift = sizeof(uns) - 1 - modulo;
150 #endif
151                         if (!modulo)
152                                 hash = ROL(hash, SHIFT_BITS);
153                         if (!s[i])
154                                 break;
155                         hash ^= s[i] << (shift * 8);
156                 }
157                 return hash;
158         }
159 }
160
161 uns
162 hash_block(const byte *str, uns len)
163 {
164         uns shift = UNALIGNED_PART(str, uns);
165         if (!shift)
166                 return hash_block_aligned(str, len);
167         else
168         {
169                 uns hash = 0;
170                 uns i;
171                 for (i=0; ; i++)
172                 {
173                         uns modulo = i % sizeof(uns);
174                         uns shift;
175 #ifdef  CPU_LITTLE_ENDIAN
176                         shift = modulo;
177 #else
178                         shift = sizeof(uns) - 1 - modulo;
179 #endif
180                         if (!modulo)
181                                 hash = ROL(hash, SHIFT_BITS);
182                         if (i >= len)
183                                 break;
184                         hash ^= str[i] << (shift * 8);
185                 }
186                 return hash;
187         }
188 }
189 #endif
190
191 uns
192 hash_string_nocase(const char *str)
193 {
194         const byte *s = str;
195         uns hash = 0;
196         uns i;
197         for (i=0; ; i++)
198         {
199                 uns modulo = i % sizeof(uns);
200                 uns shift;
201 #ifdef  CPU_LITTLE_ENDIAN
202                 shift = modulo;
203 #else
204                 shift = sizeof(uns) - 1 - modulo;
205 #endif
206                 if (!modulo)
207                         hash = ROL(hash, SHIFT_BITS);
208                 if (!s[i])
209                         break;
210                 hash ^= Cupcase(s[i]) << (shift * 8);
211         }
212         return hash;
213 }