File Coverage

MinPerfHashTwoLevel.xs
Criterion Covered Total %
statement 479 547 87.5
branch 245 424 57.7
condition n/a
subroutine n/a
pod n/a
total 724 971 74.5


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5              
6             #define NEED_newRV_noinc
7             #define NEED_sv_2pv_flags
8             #include "ppport.h"
9             #include
10             #include
11             #include
12             #include
13             #include
14             #include
15             #include "mph2l.h"
16             #include "mph_hv_macro.h"
17             #include "mph_siphash.h"
18              
19             #define MAX_VARIANT 5
20             #define MIN_VARIANT 5
21              
22             MPH_STATIC_INLINE void
23 2111676           sv_set_from_bucket(pTHX_ SV *sv, U8 *strs, const U32 ofs, const U32 len, const U32 idx, const U8 *flags, const U32 bits, const U8 utf8_default, const U8 utf8_default_shift) {
24             U8 *ptr;
25             U8 is_utf8;
26 2111676 50         if (ofs) {
27 2111676           ptr= (strs) + (ofs);
28 2111676 100         if (utf8_default) {
29 2108358           is_utf8= utf8_default >> utf8_default_shift;
30             } else {
31 2111676           GETBITS(is_utf8,flags,idx,bits);
32             }
33             } else {
34 0           ptr= 0;
35 0           is_utf8= 0;
36             }
37             /* note that sv_setpvn() will cause the sv to
38             * become undef if ptr is 0 */
39 2111676           sv_setpvn_mg((sv),ptr,len);
40 2111676 100         if (is_utf8 > 1) {
41 18           sv_utf8_upgrade(sv);
42             }
43             else
44 2111658 100         if (is_utf8) {
45 3540           SvUTF8_on(sv);
46             }
47             else
48 2108118 50         if (ptr) {
49 2108118           SvUTF8_off(sv);
50             }
51 2111676           }
52              
53             MPH_STATIC_INLINE int
54 905274           lookup_bucket(pTHX_ struct mph_header *mph, U32 index, SV *key_sv, SV *val_sv)
55             {
56             struct mph_bucket *bucket;
57             U8 *strs;
58 905274           U8 *mph_u8= (U8*)mph;
59 905274           U64 gf= mph->general_flags;
60 905274 100         if (index >= mph->num_buckets) {
61 270           return 0;
62             }
63 905004           bucket= (struct mph_bucket *)((char *)mph + mph->table_ofs) + index;
64 905004           strs= (U8 *)mph + mph->str_buf_ofs;
65 905004 50         if (val_sv) {
66 0           sv_set_from_bucket(aTHX_ val_sv,strs,bucket->val_ofs,bucket->val_len,index,mph_u8 + mph->val_flags_ofs,1,
67             gf & MPH_VALS_ARE_SAME_UTF8NESS_MASK, MPH_VALS_ARE_SAME_UTF8NESS_SHIFT);
68             }
69 905004 50         if (key_sv) {
70 905004           sv_set_from_bucket(aTHX_ key_sv,strs,bucket->key_ofs,bucket->key_len,index,mph_u8 + mph->key_flags_ofs,2,
71             gf & MPH_KEYS_ARE_SAME_UTF8NESS_MASK, MPH_KEYS_ARE_SAME_UTF8NESS_SHIFT);
72             }
73 905004           return 1;
74             }
75              
76             MPH_STATIC_INLINE int
77 1206672           lookup_key(pTHX_ struct mph_header *mph, SV *key_sv, SV *val_sv)
78             {
79 1206672           U8 *strs= (U8 *)mph + mph->str_buf_ofs;
80 1206672           struct mph_bucket *buckets= (struct mph_bucket *) ((char *)mph + mph->table_ofs);
81             struct mph_bucket *bucket;
82 1206672           U8 *state= (char *)mph + mph->state_ofs;
83             STRLEN key_len;
84             U8 *key_pv;
85             U64 h0;
86             U32 h1;
87             U32 h2;
88             U32 index;
89             U8 *got_key_pv;
90             STRLEN got_key_len;
91              
92 1206672 100         if (SvUTF8(key_sv)) {
93 1992           SV *tmp= sv_2mortal(newSVsv(key_sv));
94 1992           sv_utf8_downgrade(tmp,1);
95 1992           key_sv= tmp;
96             }
97 1206672 50         key_pv= SvPV(key_sv,key_len);
98 1206672           h0= mph_hash_with_state(state,key_pv,key_len);
99 1206672           h1= h0 >> 32;
100 1206672           index= h1 % mph->num_buckets;
101              
102 1206672           bucket= buckets + index;
103 1206672 50         if (!bucket->xor_val)
104 0           return 0;
105            
106 1206672           h2= h0 & 0xFFFFFFFF;
107 1206672 100         if ( bucket->index < 0 ) {
108 443624           index = -bucket->index-1;
109             } else {
110 763048           HASH2INDEX(index,h2,bucket->xor_val,mph->num_buckets);
111             }
112 1206672           bucket= buckets + index;
113 1206672           got_key_pv= strs + bucket->key_ofs;
114 1206672 50         if (bucket->key_len == key_len && memEQ(key_pv,got_key_pv,key_len)) {
    50          
115 1206672 50         if (val_sv) {
116 1206672           U64 gf= mph->general_flags;
117 1206672           sv_set_from_bucket(aTHX_ val_sv,strs,bucket->val_ofs,bucket->val_len,index,((U8*)mph)+mph->val_flags_ofs,1,
118             gf & MPH_VALS_ARE_SAME_UTF8NESS_MASK, MPH_VALS_ARE_SAME_UTF8NESS_SHIFT);
119             }
120 1206672           return 1;
121             }
122 1206672           return 0;
123             }
124              
125             IV
126 366           mph_mmap(pTHX_ char *file, struct mph_obj *obj, SV *error, U32 flags) {
127             struct stat st;
128             struct mph_header *head;
129 366           int fd = open(file, O_RDONLY, 0);
130             void *ptr;
131             U32 alignment;
132              
133 366 50         if (error)
134 366           sv_setpvs(error,"");
135 366 50         if (fd < 0) {
136 0 0         if (error)
137 0           sv_setpvf(error,"file '%s' could not be opened for read", file);
138 0           return MPH_MOUNT_ERROR_OPEN_FAILED;
139             }
140 366 50         if (fstat(fd,&st)==-1) {
141 0 0         if (error)
142 0           sv_setpvf(error,"file '%s' could not be fstat()ed", file);
143 0           return MPH_MOUNT_ERROR_FSTAT_FAILED;
144             }
145 366 50         if (st.st_size < sizeof(struct mph_header)) {
146 0 0         if (error)
147 0           sv_setpvf(error,"file '%s' is too small to be a valid PH2L file", file);
148 0           return MPH_MOUNT_ERROR_TOO_SMALL;
149             }
150 366           ptr = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED | MPH_MAP_POPULATE, fd, 0);
151 366           close(fd); /* kernel holds its own refcount on the file, we do not need to keep it open */
152 366 50         if (ptr == MAP_FAILED) {
153 0 0         if (error)
154 0           sv_setpvf(error,"failed to create mapping to file '%s'", file);
155 0           return MPH_MOUNT_ERROR_MAP_FAILED;
156             }
157              
158 366           obj->bytes= st.st_size;
159 366           obj->header= head= (struct mph_header*)ptr;
160 366 100         if (head->magic_num != MAGIC_DECIMAL) {
161 5 50         if (head->magic_num == MAGIC_BIG_ENDIAN_DECIMAL) {
162 0 0         if (error)
163 0           sv_setpvf(error,"this is a big-endian machine, cant handle PH2L files here");
164             }
165 5 50         if (error)
166 5           sv_setpvf(error,"file '%s' is not a PH2L file", file);
167 5           return MPH_MOUNT_ERROR_BAD_MAGIC;
168             }
169 361 50         if (head->variant < MIN_VARIANT) {
170 0 0         if (error)
171 0           sv_setpvf(error,"unsupported old version '%d' in '%s'", head->variant, file);
172 0           return MPH_MOUNT_ERROR_BAD_VERSION;
173             }
174 361 100         if (head->variant > MAX_VARIANT) {
175 4 50         if (error)
176 4           sv_setpvf(error,"unknown version '%d' in '%s'", head->variant, file);
177 4           return MPH_MOUNT_ERROR_BAD_VERSION;
178             }
179 357           alignment = sizeof(U64);
180              
181 357 50         if (st.st_size % alignment) {
182 0 0         if (error)
183 0           sv_setpvf(error,"file '%s' does not have a size which is a multiple of 16 bytes", file);
184 0           return MPH_MOUNT_ERROR_BAD_SIZE;
185             }
186 357 100         if (
187 354 100         head->table_ofs < head->state_ofs ||
188 350 100         head->key_flags_ofs < head->table_ofs ||
189 346 100         head->val_flags_ofs < head->key_flags_ofs ||
190 342 100         head->str_buf_ofs < head->val_flags_ofs ||
191 342           st.st_size < head->str_buf_ofs
192             ) {
193 18 50         if (error)
194 18           sv_setpvf(error,"corrupt header offsets in '%s'", file);
195 18           return MPH_MOUNT_ERROR_BAD_OFFSETS;
196             }
197 339 100         if (flags & MPH_F_VALIDATE) {
198 249           char *start= ptr;
199 249           char *state_pv= start + head->state_ofs;
200 249           char *str_buf_start= start + head->str_buf_ofs;
201 249           char *str_buf_end= start + st.st_size;
202              
203 249           U64 have_file_checksum= mph_hash_with_state(state_pv, start, st.st_size - sizeof(U64));
204 249           U64 want_file_checksum= *((U64 *)(str_buf_end - sizeof(U64)));
205 249 100         if (have_file_checksum != want_file_checksum) {
206 158 50         if (error)
207 158           sv_setpvf(error,"file checksum '%016lx' != '%016lx' in file '%s'",
208             have_file_checksum,want_file_checksum,file);
209 158           return MPH_MOUNT_ERROR_CORRUPT_FILE;
210             }
211             }
212 366           return head->variant;
213             }
214              
215             void
216 181           mph_munmap(struct mph_obj *obj) {
217 181           munmap(obj->header,obj->bytes);
218 181           }
219              
220             STRLEN
221 603402           normalize_with_flags(pTHX_ SV *sv, SV *normalized_sv, SV *is_utf8_sv, int downgrade) {
222             STRLEN len;
223 603402 100         if (SvROK(sv)) {
224 1           croak("Error: Not expecting a reference value in source hash");
225             }
226 603401           sv_setsv(normalized_sv,sv);
227 603401 50         if (SvOK(sv)) {
    0          
    0          
228             STRLEN pv_len;
229 603401 100         char *pv= SvPV(sv,pv_len);
230 603401 100         if (pv_len > 0xFFFF)
231 1           croak("Error: String in source hash is too long to store, max length is %u got length %lu", 0xFFFF, pv_len);
232 603400 100         if (SvUTF8(sv)) {
233 1030 100         if (downgrade)
234 506           sv_utf8_downgrade(normalized_sv,1);
235 1030 100         if (SvUTF8(normalized_sv)) {
236 1021           SvUTF8_off(normalized_sv);
237 1021           sv_setiv(is_utf8_sv,1);
238             } else {
239 9           sv_setiv(is_utf8_sv,2);
240             }
241             }
242 603400           return pv_len;
243             } else {
244 0           sv_setiv(is_utf8_sv, 0);
245 0           return 0;
246             }
247             }
248              
249             U32
250 546           _roundup(const U32 n, const U32 s) {
251 546           const U32 r= n % s;
252 546 100         if (r) {
253 246           return n + s - r;
254             } else {
255 300           return n;
256             }
257             }
258              
259             START_MY_CXT
260              
261             I32
262 2164019           _compare(pTHX_ SV *a, SV *b) {
263             dMY_CXT;
264 2164019           HE *a_he= hv_fetch_ent_with_keysv((HV*)SvRV(a),MPH_KEYSV_KEY_NORMALIZED,0);
265 2164019           HE *b_he= hv_fetch_ent_with_keysv((HV*)SvRV(b),MPH_KEYSV_KEY_NORMALIZED,0);
266              
267 2164019           return sv_cmp(HeVAL(a_he),HeVAL(b_he));
268             }
269              
270             U32
271 95           normalize_source_hash(pTHX_ HV *source_hv, AV *keys_av, U32 compute_flags, SV *buf_length_sv, char *state_pv) {
272             dMY_CXT;
273             HE *he;
274 95           U32 buf_length= 0;
275             U32 ctr;
276              
277 95           hv_iterinit(source_hv);
278 301794 100         while (he= hv_iternext(source_hv)) {
279 301701           SV *val_sv= HeVAL(he);
280             SV *val_normalized_sv;
281             SV *val_is_utf8_sv;
282              
283             SV *key_sv;
284             SV *key_normalized_sv;
285             SV *key_is_utf8_sv;
286             HV *hv;
287             U8 *key_pv;
288             STRLEN key_len;
289             U64 h0;
290              
291 301701 50         if (!val_sv) croak("panic: no sv for value?");
292 301701 50         if (!SvOK(val_sv) && (compute_flags & MPH_F_FILTER_UNDEF)) continue;
    0          
    0          
    0          
293              
294 301701           hv= newHV();
295 301701           val_normalized_sv= newSV(0);
296 301701           val_is_utf8_sv= newSVuv(0);
297              
298 301701           key_sv= newSVhek(HeKEY_hek(he));
299 301701           key_normalized_sv= newSV(0);
300 301701           key_is_utf8_sv= newSVuv(0);
301              
302 301701           hv_ksplit(hv,15);
303 301701           hv_store_ent_with_keysv(hv,MPH_KEYSV_KEY, key_sv);
304 301701           hv_store_ent_with_keysv(hv,MPH_KEYSV_KEY_NORMALIZED, key_normalized_sv);
305 301701           hv_store_ent_with_keysv(hv,MPH_KEYSV_KEY_IS_UTF8, key_is_utf8_sv);
306 301701           hv_store_ent_with_keysv(hv,MPH_KEYSV_VAL, SvREFCNT_inc_simple_NN(val_sv));
307 301701           hv_store_ent_with_keysv(hv,MPH_KEYSV_VAL_NORMALIZED, val_normalized_sv);
308 301701           hv_store_ent_with_keysv(hv,MPH_KEYSV_VAL_IS_UTF8, val_is_utf8_sv);
309             /* install everything into the keys_av just in case normalize_with_flags() dies */
310 301701           av_push(keys_av,newRV_noinc((SV*)hv));
311              
312 301701           buf_length += normalize_with_flags(aTHX_ key_sv, key_normalized_sv, key_is_utf8_sv, 1);
313 301701           buf_length += normalize_with_flags(aTHX_ val_sv, val_normalized_sv, val_is_utf8_sv, 0);
314              
315 301699 50         key_pv= (U8 *)SvPV(key_normalized_sv,key_len);
316 301699           h0= mph_hash_with_state(state_pv,key_pv,key_len);
317              
318 301699           hv_store_ent_with_keysv(hv,MPH_KEYSV_H0, newSVuv(h0));
319             }
320 93 50         if (buf_length_sv)
321 93           sv_setuv(buf_length_sv, buf_length);
322              
323             /* we now know how many keys there are, and what the max_xor_val should be */
324 93           return av_top_index(keys_av)+1;
325             }
326              
327             void
328 93           find_first_level_collisions(pTHX_ U32 bucket_count, AV *keys_av, AV *keybuckets_av, AV *h2_packed_av) {
329             dMY_CXT;
330             U32 i;
331 301790 100         for (i=0; i
332             U64 h0;
333             U32 h1;
334             U32 h2;
335             U32 idx1;
336             SV **got_psv;
337             SV* h0_sv;
338             HE* h0_he;
339             HV *hv;
340             AV *av;
341 301697           got_psv= av_fetch(keys_av,i,0);
342 301697 50         if (!got_psv || !SvROK(*got_psv)) croak("panic: bad item in keys_av");
    50          
343 301697           hv= (HV *)SvRV(*got_psv);
344 301697           h0_he= hv_fetch_ent_with_keysv(hv,MPH_KEYSV_H0,0);
345 301697 50         if (!h0_he) croak("panic: no h0_he?");
346 301697           h0_sv= HeVAL(h0_he);
347 301697 100         h0= SvUV(h0_sv);
348              
349 301697           h1= h0 >> 32;
350 301697           h2= h0 & 0xFFFFFFFF;
351 301697           idx1= h1 % bucket_count;
352 301697           got_psv= av_fetch(h2_packed_av,idx1,1);
353 301697 50         if (!got_psv)
354 0           croak("panic: out of memory creating new h2_packed_av element");
355 301697 100         if (!SvPOK(*got_psv))
356 190715           sv_setpvs(*got_psv,"");
357 301697           sv_catpvn(*got_psv, (char *)&h2, 4);
358              
359 301697           got_psv= av_fetch(keybuckets_av,idx1,1);
360 301697 50         if (!got_psv)
361 0           croak("panic: out of memory creating new keybuckets_av element");
362              
363 301697 100         if (!SvROK(*got_psv)) {
364 190715           av= newAV();
365 190715           sv_upgrade(*got_psv,SVt_RV);
366 190715           SvRV_set(*got_psv,(SV *)av);
367 190715           SvROK_on(*got_psv);
368             } else {
369 110982           av= (AV *)SvRV(*got_psv);
370             }
371              
372 301697           av_push(av,newRV_inc((SV*)hv));
373             }
374 93           }
375              
376             AV *
377 93           idx_by_length(pTHX_ AV *keybuckets_av) {
378             U32 i;
379 93           U32 keybuckets_count= av_top_index(keybuckets_av) + 1;
380 93           AV *by_length_av= (AV*)sv_2mortal((SV*)newAV());
381 301737 100         for( i = 0 ; i < keybuckets_count ; i++ ) {
382 301644           SV **got= av_fetch(keybuckets_av,i,0);
383             AV *keys_av;
384             SV *keys_ref;
385             AV *target_av;
386             IV len;
387 301644 100         if (!got) continue;
388 190715           keys_av= (AV *)SvRV(*got);
389 190715           len= av_top_index(keys_av) + 1;
390 190715 50         if (len<1) continue;
391              
392 190715           got= av_fetch(by_length_av,len,1);
393 190715 100         if (SvPOK(*got)) {
394 190396           sv_catpvn(*got,(char *)&i,4);
395             } else {
396 319           sv_setpvn(*got,(char *)&i,4);
397             }
398             }
399 93           return by_length_av;
400             }
401              
402 190715           void set_xor_val_in_buckets(pTHX_ U32 xor_val, AV *buckets_av, U32 idx1, U32 *idx_start, char *is_used, AV *keys_in_bucket_av) {
403             dMY_CXT;
404             U32 *idx2;
405             HV *idx1_hv;
406             U32 i;
407 190715           U32 keys_in_bucket_count= av_top_index(keys_in_bucket_av) + 1;
408              
409 190715           SV **buckets_rvp= av_fetch(buckets_av, idx1, 1);
410 190715 50         if (!buckets_rvp) croak("panic: out of memory in buckets_av lvalue fetch");
411 190715 100         if (!SvROK(*buckets_rvp)) {
412 84805           idx1_hv= newHV();
413 84805 50         if (!idx1_hv) croak("panic: out of memory creating new hash in buckets_av idx %u",idx1);
414 84805           sv_upgrade(*buckets_rvp,SVt_RV);
415 84805           SvRV_set(*buckets_rvp,(SV *)idx1_hv);
416 84805           SvROK_on(*buckets_rvp);
417             } else {
418 105910           idx1_hv= (HV *)SvRV(*buckets_rvp);
419             }
420              
421 190715 50         hv_setuv_with_keysv(idx1_hv,MPH_KEYSV_XOR_VAL,xor_val);
422 190715 50         hv_setuv_with_keysv(idx1_hv,MPH_KEYSV_H1_KEYS,keys_in_bucket_count);
423              
424             /* update used */
425 492412 100         for (i= 0, idx2= idx_start; i < keys_in_bucket_count; i++,idx2++) {
426             HV *idx2_hv;
427             HV *keys_hv;
428              
429             SV **keys_rvp;
430             SV **buckets_rvp;
431              
432 301697           keys_rvp= av_fetch(keys_in_bucket_av, i, 0);
433 301697 50         if (!keys_rvp) croak("panic: no key_info in bucket %d", i);
434 301697           keys_hv= (HV *)SvRV(*keys_rvp);
435              
436 301697           buckets_rvp= av_fetch(buckets_av, *idx2, 1);
437 301697 50         if (!buckets_rvp) croak("panic: out of memory in lvalue fetch to buckets_av");
438              
439 301697 100         if (!SvROK(*buckets_rvp)) {
440 216892           sv_upgrade(*buckets_rvp,SVt_RV);
441             } else {
442 84805           idx2_hv= (HV *)SvRV(*buckets_rvp);
443              
444 84805 50         hv_copy_with_keysv(idx2_hv,keys_hv,MPH_KEYSV_XOR_VAL);
445 84805 50         hv_copy_with_keysv(idx2_hv,keys_hv,MPH_KEYSV_H1_KEYS);
446 84805           SvREFCNT_dec(idx2_hv);
447             }
448              
449 301697           SvRV_set(*buckets_rvp,(SV*)keys_hv);
450 301697           SvROK_on(*buckets_rvp);
451 301697           SvREFCNT_inc(keys_hv);
452              
453 301697 50         hv_setuv_with_keysv(keys_hv,MPH_KEYSV_IDX,*idx2);
454              
455 301697           is_used[*idx2] = 1;
456             }
457 190715           }
458              
459             U32
460 226           solve_collisions(pTHX_ U32 bucket_count, U32 max_xor_val, SV *idx1_packed_sv, AV *h2_packed_av, AV *keybuckets_av, U32 variant, char *is_used, U32 *idx2_start,AV *buckets_av) {
461             STRLEN idx1_packed_sv_len;
462 226 50         U32 *idx1_start= (U32 *)SvPV(idx1_packed_sv,idx1_packed_sv_len);
463             U32 *idx1_ptr;
464 226           U32 *idx1_end= idx1_start + (idx1_packed_sv_len / sizeof(U32));
465              
466 80021 100         for (idx1_ptr= idx1_start; idx1_ptr < idx1_end; idx1_ptr++) {
467 79795           U32 idx1= *idx1_ptr;
468             SV *h2_sv;
469             AV *keys_in_bucket_av;
470 79795           U32 xor_val= 0;
471             STRLEN h2_strlen;
472             U32 *h2_start;
473             STRLEN keys_in_bucket_count;
474             U32 *h2_end;
475             SV **got;
476              
477 79795           got= av_fetch(h2_packed_av, idx1, 0);
478 79795 50         if (!got)
479 0           croak("panic: no h2_buckets for idx %u",idx1);
480 79795           h2_sv= *got;
481              
482 79795           got= av_fetch(keybuckets_av, idx1, 0);
483 79795 50         if (!got)
484 0           croak("panic: no keybuckets_av for idx %u",idx1);
485 79795           keys_in_bucket_av= (AV *)SvRV(*got);
486              
487 79795 50         h2_start= (U32 *)SvPV(h2_sv,h2_strlen);
488 79795           keys_in_bucket_count= h2_strlen / sizeof(U32);
489 79795           h2_end= h2_start + keys_in_bucket_count;
490              
491             next_xor_val:
492             while (1) {
493 244616           U32 *h2_ptr= h2_start;
494 244616           U32 *idx2_ptr= idx2_start;
495 244616 50         if (xor_val == max_xor_val) {
496 0           warn("failed to resolve collision idx1: %d\n",idx1);
497 0 0         while (h2_ptr < h2_end)
498 0           warn("hash: %016x\n", *h2_ptr++);
499 0           return idx1 + 1;
500             } else {
501 244616           xor_val++;
502             }
503 498547 100         while (h2_ptr < h2_end) {
504             U32 idx2;
505             U32 *check_idx;
506 418752           HASH2INDEX(idx2,*h2_ptr,xor_val,bucket_count);
507 418752 100         if (is_used[idx2])
508 164775           goto next_xor_val;
509 409464 100         for (check_idx= idx2_start; check_idx < idx2_ptr; check_idx++) {
510 155533 100         if (*check_idx == idx2)
511 46           goto next_xor_val;
512             }
513 253931           *idx2_ptr= idx2;
514 253931           h2_ptr++;
515 253931           idx2_ptr++;
516             }
517 79795           break;
518             }
519 79795           set_xor_val_in_buckets(aTHX_ xor_val, buckets_av, idx1, idx2_start, is_used, keys_in_bucket_av);
520             }
521 226           return 0;
522             }
523              
524             U32
525 93           place_singletons(pTHX_ U32 bucket_count, SV *idx1_packed_sv, AV *keybuckets_av, char *is_used, U32 *idx2_start, AV *buckets_av) {
526             STRLEN idx1_packed_sv_len;
527 93 50         U32 *idx1_start= (U32 *)SvPV(idx1_packed_sv,idx1_packed_sv_len);
528             U32 *idx1_ptr;
529 93           U32 *idx1_end= idx1_start + (idx1_packed_sv_len / sizeof(U32));
530              
531 93           U32 singleton_pos= 0;
532              
533 111013 100         for (idx1_ptr= idx1_start; idx1_ptr < idx1_end; idx1_ptr++) {
534 110920           U32 idx1= *idx1_ptr;
535             AV *keys_in_bucket_av;
536             U32 xor_val;
537             SV **got;
538              
539 412354 50         while (singleton_pos < bucket_count && is_used[singleton_pos]) {
    100          
540 301434           singleton_pos++;
541             }
542 110920 50         if (singleton_pos == bucket_count) {
543 0           warn("failed to place singleton! idx: %d",idx1);
544 0           return idx1 + 1;
545             }
546              
547 110920           xor_val= (U32)(-singleton_pos-1);
548 110920           got= av_fetch(keybuckets_av, idx1, 0);
549 110920 50         if (!got)
550 0           croak("panic: no keybuckets_av for idx %u",idx1);
551 110920           keys_in_bucket_av= (AV *)SvRV(*got);
552 110920           *idx2_start= singleton_pos;
553 110920           set_xor_val_in_buckets(aTHX_ xor_val, buckets_av, idx1, idx2_start, is_used, keys_in_bucket_av);
554             }
555 93           return 0;
556             }
557              
558             U32
559 93           solve_collisions_by_length(pTHX_ U32 bucket_count, U32 max_xor_val, AV *by_length_av, AV *h2_packed_av, AV *keybuckets_av, U32 variant, AV *buckets_av) {
560 93           U32 bad_idx= 0;
561 93           I32 singleton_pos= 0;
562             IV len_idx;
563             char *is_used;
564             U32 *idx2_start;
565              
566             /* this is used to quickly tell if we have used a particular bucket yet */
567 93           Newxz(is_used,bucket_count,char);
568 93           SAVEFREEPV(is_used);
569              
570             /* used to keep track the indexes that a set of keys map into
571             * stored in an SV just because - we actually treat it as an array of U32 */
572 93 50         Newxz(idx2_start, av_top_index(by_length_av)+1, U32);
573 93           SAVEFREEPV(idx2_start);
574              
575             /* now loop through and process the keysets from most collisions to least */
576 432 100         for (len_idx= av_top_index(by_length_av); len_idx > 0 && !bad_idx; len_idx--) {
    50          
577 339           SV **idx1_packed_sv= av_fetch(by_length_av, len_idx, 0);
578             /* deal with the possibility that there are gaps in the length grouping,
579             * for instance we might have some 13 way collisions and some 11 way collisions
580             * without any 12-way collisions. (this should be rare - but is possible) */
581 339 100         if (!idx1_packed_sv || !SvPOK(*idx1_packed_sv))
    50          
582 20           continue;
583              
584 319 100         if (len_idx == 1) {
585 93           bad_idx= place_singletons(aTHX_ bucket_count, *idx1_packed_sv, keybuckets_av,
586             is_used, idx2_start, buckets_av);
587             } else {
588 226           bad_idx= solve_collisions(aTHX_ bucket_count, max_xor_val, *idx1_packed_sv, h2_packed_av, keybuckets_av,
589             variant, is_used, idx2_start, buckets_av);
590             }
591             }
592 93           return bad_idx;
593             }
594              
595             #define MY_CXT_KEY "Algorithm::MinPerfHashTwoLevel::_stash" XS_VERSION
596              
597             #define SETOFS(i,he,table,key_ofs,key_len,str_buf_start,str_buf_pos,str_buf_end,str_ofs_hv) \
598             STMT_START { \
599             if (he) { \
600             SV *sv= HeVAL(he); \
601             if (SvOK(sv)) { \
602             STRLEN pv_len; \
603             char *pv; \
604             SV *ofs_sv; \
605             if (flags & MPH_F_NO_DEDUPE) { \
606             ofs_sv= NULL; \
607             } else { \
608             HE *ofs= hv_fetch_ent(str_ofs_hv,sv,1,0); \
609             ofs_sv= ofs ? HeVAL(ofs) : NULL; \
610             if (!ofs_sv) \
611             croak("panic: out of memory getting str ofs for " #he "for %u",i); \
612             } \
613             if (ofs_sv && SvOK(ofs_sv)){ \
614             table[i].key_ofs= SvUV(ofs_sv); \
615             table[i].key_len= sv_len(sv); \
616             } else { \
617             pv= SvPV(sv,pv_len); \
618             table[i].key_len= pv_len; \
619             if (pv_len) { \
620             table[i].key_ofs= str_buf_pos - str_buf_start; \
621             if (str_buf_pos + pv_len > str_buf_end) \
622             croak("panic: string buffer too small in SETOFS, something went horribly wrong."); \
623             Copy(pv,str_buf_pos,pv_len,char); \
624             str_buf_pos += pv_len; \
625             } else { \
626             table[i].key_ofs= 1; \
627             } \
628             if (ofs_sv) \
629             sv_setuv(ofs_sv,table[i].key_ofs); \
630             } \
631             } else { \
632             table[i].key_ofs= 0; \
633             table[i].key_len= 0; \
634             } \
635             } else { \
636             croak("no " #he " for %u",i); \
637             } \
638             } STMT_END
639              
640              
641             MODULE = Algorithm::MinPerfHashTwoLevel PACKAGE = Algorithm::MinPerfHashTwoLevel
642              
643             BOOT:
644             {
645 4           MPH_INIT_ALL_KEYSV();
646             }
647              
648             UV
649             hash_with_state(str_sv,state_sv)
650             SV* str_sv
651             SV* state_sv
652             PROTOTYPE: $$
653             CODE:
654             {
655             STRLEN str_len;
656             STRLEN state_len;
657             U8 *state_pv;
658 3 50         U8 *str_pv= (U8 *)SvPV(str_sv,str_len);
659 3 50         state_pv= (U8 *)SvPV(state_sv,state_len);
660 3 50         if (state_len != MPH_STATE_BYTES) {
661 0           croak("Error: state vector must be at exactly %d bytes",(int)MPH_SEED_BYTES);
662             }
663 3           RETVAL= mph_hash_with_state(state_pv,str_pv,str_len);
664             }
665             OUTPUT:
666             RETVAL
667              
668              
669             SV *
670             seed_state(base_seed_sv)
671             SV* base_seed_sv
672             PROTOTYPE: $
673             CODE:
674             {
675             STRLEN seed_len;
676             STRLEN state_len;
677             U8 *seed_pv;
678             U8 *state_pv;
679             SV *seed_sv;
680 96 50         if (!SvOK(base_seed_sv))
    0          
    0          
681 0           croak("Error: seed must be defined");
682 96 50         if (SvROK(base_seed_sv))
683 0           croak("Error: seed should not be a reference");
684 96           seed_sv= base_seed_sv;
685 96 50         seed_pv= (U8 *)SvPV(seed_sv,seed_len);
686              
687 96 50         if (seed_len != MPH_SEED_BYTES) {
688 0 0         if (SvREADONLY(base_seed_sv)) {
689 0 0         if (seed_len < MPH_SEED_BYTES) {
690 0           warn("seed passed into seed_state() is readonly and too short, argument has been right padded with %d nulls",
691             (int)(MPH_SEED_BYTES - seed_len));
692             }
693 0 0         else if (seed_len > MPH_SEED_BYTES) {
694 0           warn("seed passed into seed_state() is readonly and too long, using only the first %d bytes",
695             (int)MPH_SEED_BYTES);
696             }
697 0           seed_sv= sv_2mortal(newSVsv(base_seed_sv));
698             }
699 0 0         if (seed_len < MPH_SEED_BYTES) {
700 0           sv_grow(seed_sv,MPH_SEED_BYTES+1);
701 0 0         while (seed_len < MPH_SEED_BYTES) {
702 0           seed_pv[seed_len] = 0;
703 0           seed_len++;
704             }
705             }
706 0           SvCUR_set(seed_sv,MPH_SEED_BYTES);
707 0 0         seed_pv= (U8 *)SvPV(seed_sv,seed_len);
708             } else {
709 96           seed_sv= base_seed_sv;
710             }
711              
712 96           RETVAL= newSV(MPH_STATE_BYTES+1);
713 96           SvCUR_set(RETVAL,MPH_STATE_BYTES);
714 96           SvPOK_on(RETVAL);
715 96 50         state_pv= (U8 *)SvPV(RETVAL,state_len);
716 96           mph_seed_state(seed_pv,state_pv);
717             }
718             OUTPUT:
719             RETVAL
720              
721              
722             UV
723             compute_xs(self_hv)
724             HV *self_hv
725             PREINIT:
726             dMY_CXT;
727             PROTOTYPE: \%\@
728             CODE:
729             {
730             U8 *state_pv;
731             STRLEN state_len;
732             HE *he;
733              
734             IV len_idx;
735              
736             U32 bucket_count;
737             U32 max_xor_val;
738             U32 i;
739              
740             U32 variant;
741             U32 compute_flags;
742              
743             SV* buf_length_sv;
744              
745             HV* source_hv;
746              
747             AV *buckets_av;
748             AV *keys_av;
749             AV *by_length_av;
750             AV *keybuckets_av;
751             AV *h2_packed_av;
752              
753 95           RETVAL = 0;
754              
755             /**** extract the various reference data we need from $self */
756              
757 95           he= hv_fetch_ent_with_keysv(self_hv,MPH_KEYSV_VARIANT,0);
758 95 50         if (he) {
759 95 50         variant= SvUV(HeVAL(he));
760             } else {
761 0           croak("panic: no variant in self?");
762             }
763              
764 95           he= hv_fetch_ent_with_keysv(self_hv,MPH_KEYSV_COMPUTE_FLAGS,0);
765 95 50         if (he) {
766 95 50         compute_flags= SvUV(HeVAL(he));
767             } else {
768 0           croak("panic: no compute_flags in self?");
769             }
770              
771 95           he= hv_fetch_ent_with_keysv(self_hv,MPH_KEYSV_STATE,0);
772 95 50         if (he) {
773 95           SV *state_sv= HeVAL(he);
774 95 50         state_pv= (U8 *)SvPV(state_sv,state_len);
775 95 50         if (state_len != MPH_STATE_BYTES) {
776 95           croak("Error: state vector must be at exactly %d bytes",(int)MPH_SEED_BYTES);
777             }
778             } else {
779 0           croak("panic: no state in self?");
780             }
781              
782 95           he= hv_fetch_ent_with_keysv(self_hv,MPH_KEYSV_BUF_LENGTH,1);
783 95 50         if (he) {
784 95           buf_length_sv= HeVAL(he);
785             } else {
786 0           croak("panic: out of memory in lvalue fetch for 'buf_length' in self");
787             }
788              
789 95           he= hv_fetch_ent_with_keysv(self_hv,MPH_KEYSV_SOURCE_HASH,0);
790 95 50         if (he) {
791 95           source_hv= (HV*)SvRV(HeVAL(he));
792             } else {
793 0           croak("panic: no source_hash in self");
794             }
795              
796 95           he= hv_fetch_ent_with_keysv(self_hv,MPH_KEYSV_BUCKETS,1);
797 95 50         if (he) {
798 95           SV *rv= HeVAL(he);
799 95 50         if (SvROK(rv)) {
800 0           AV *old_buckets_av= (AV*)SvRV(rv);
801 0           SvREFCNT_dec(old_buckets_av);
802             } else {
803 95           sv_upgrade(rv, SVt_RV);
804             }
805 95           buckets_av= newAV();
806 95           SvRV_set(rv,(SV*)buckets_av);
807 95           SvROK_on(rv);
808             } else {
809 0           croak("panic: out of memory in lvalue fetch for 'buckets' in self");
810             }
811              
812             /**** build an array of hashes in keys_av based on the normalized contents of source_hv */
813 95           keys_av= (AV *)sv_2mortal((SV*)newAV());
814 95           bucket_count= normalize_source_hash(aTHX_ source_hv, keys_av, compute_flags, buf_length_sv, state_pv);
815 93           max_xor_val= INT32_MAX;
816              
817             /* if the caller wants deterministic results we sort the keys_av
818             * before we compute collisions - depending on the order we process
819             * the keys we might resolve the collisions differently */
820 93 100         if (compute_flags & MPH_F_DETERMINISTIC)
821 46           sortsv(AvARRAY(keys_av),bucket_count,_compare);
822              
823             /**** find the collisions from the data we just computed, build an AoAoH and AoS of the
824             **** collision data */
825 93           keybuckets_av= (AV*)sv_2mortal((SV*)newAV()); /* AoAoH - hashes from keys_av */
826 93           h2_packed_av= (AV*)sv_2mortal((SV*)newAV()); /* AoS - packed h1 */
827 93           find_first_level_collisions(aTHX_ bucket_count, keys_av, keybuckets_av, h2_packed_av);
828              
829             /* Sort the buckets by size by constructing an AoS, with the outer array indexed by length,
830             * and the inner string being the list of items of that length. (Thus the contents of index
831             * 0 is empty/undef).
832             * The end result is we can process the collisions from the most keys to a bucket to the
833             * least in O(N) and not O(N log2 N).
834             *
835             * the length of the array (av_top_index+1) reflect the number of items in the bucket
836             * with the most collisions - we use this later to size some of our data structures.
837             */
838 93           by_length_av= idx_by_length(aTHX_ keybuckets_av);
839            
840 93           RETVAL= solve_collisions_by_length(aTHX_ bucket_count, max_xor_val, by_length_av, h2_packed_av, keybuckets_av,
841             variant, buckets_av);
842             }
843             OUTPUT:
844             RETVAL
845              
846              
847              
848             MODULE = Algorithm::MinPerfHashTwoLevel PACKAGE = Tie::Hash::MinPerfHashTwoLevel::OnDisk
849              
850             SV *
851             packed_xs(variant,buf_length_sv,state_sv,comment_sv,flags,buckets_av)
852             U32 variant
853             SV* buf_length_sv
854             SV* state_sv
855             SV* comment_sv
856             AV *buckets_av
857             U32 flags
858             PREINIT:
859             dMY_CXT;
860             PROTOTYPE: $$$$$\@
861             CODE:
862             {
863 91 50         U32 buf_length= SvUV(buf_length_sv);
864 91           U32 bucket_count= av_top_index(buckets_av) + 1;
865 91           U32 header_rlen= _roundup(sizeof(struct mph_header),16);
866             STRLEN state_len;
867 91 50         char *state_pv= SvPV(state_sv, state_len);
868            
869 91           U32 alignment= sizeof(U64);
870 91           U32 state_rlen= _roundup(state_len,alignment);
871 91           U32 table_rlen= _roundup(sizeof(struct mph_bucket) * bucket_count,alignment);
872 91           U32 key_flags_rlen= _roundup((bucket_count * 2 + 7 ) / 8,alignment);
873 91           U32 val_flags_rlen= _roundup((bucket_count + 7) / 8,alignment);
874 182 50         U32 str_rlen= _roundup( buf_length
875             + 2
876 91 0         + ( SvOK(comment_sv) ? sv_len(comment_sv) + 1 : 1 )
    0          
877             + ( 2 + 8 ),
878             alignment );
879              
880             U32 total_size;
881 91           HV *str_ofs_hv= (HV *)sv_2mortal((SV*)newHV());
882             SV *sv_buf;
883             char *start;
884             struct mph_header *head;
885             char *state;
886             struct mph_bucket *table;
887             char *key_flags;
888             char *val_flags;
889             char *str_buf_start;
890             char *str_buf_end;
891             char *str_buf_pos;
892             U32 i;
893             STRLEN pv_len;
894             char *pv;
895 91           U32 key_is_utf8_count[3]={0,0,0};
896 91           U32 val_is_utf8_count[2]={0,0};
897             U32 used_flags;
898             U32 the_flag;
899 91           IV key_is_utf8_generic=-1;
900 91           IV val_is_utf8_generic=-1;
901              
902 301764 100         for (i= 0; i < bucket_count; i++) {
903 301673           SV **got= av_fetch(buckets_av,i,0);
904 301673           HV *hv= (HV *)SvRV(*got);
905 301673           HE *key_is_utf8_he= hv_fetch_ent_with_keysv(hv,MPH_KEYSV_KEY_IS_UTF8,0);
906 301673           HE *val_is_utf8_he= hv_fetch_ent_with_keysv(hv,MPH_KEYSV_VAL_IS_UTF8,0);
907 301673 50         key_is_utf8_count[SvUV(HeVAL(key_is_utf8_he))]++;
908 301673 50         val_is_utf8_count[SvUV(HeVAL(val_is_utf8_he))]++;
909             }
910 91           used_flags= 0;
911 91 100         if (key_is_utf8_count[0]) { the_flag= 0; used_flags++; }
912 91 100         if (key_is_utf8_count[1]) { the_flag= 1; used_flags++; }
913 91 100         if (key_is_utf8_count[2]) { the_flag= 2; used_flags++; }
914 91 100         if (used_flags == 1) {
915 67           key_is_utf8_generic= the_flag;
916 67           key_flags_rlen= 0;
917             }
918 91           used_flags= 0;
919 91 100         if (val_is_utf8_count[0]) { the_flag= 0; used_flags++; }
920 91 100         if (val_is_utf8_count[1]) { the_flag= 1; used_flags++; }
921 91 100         if (used_flags == 1) {
922 67           val_is_utf8_generic= the_flag;
923 67           val_flags_rlen= 0;
924             }
925              
926 91           total_size=
927             + header_rlen
928 91           + state_rlen
929 91           + table_rlen
930 91           + key_flags_rlen
931 91           + val_flags_rlen
932             + str_rlen
933             ;
934            
935 91           sv_buf= newSV(total_size);
936 91           SvPOK_on(sv_buf);
937 91           SvCUR_set(sv_buf,total_size);
938 91           start= SvPVX(sv_buf);
939 91           Zero(start,total_size,char);
940 91           head= (struct mph_header *)start;
941              
942 91           head->magic_num= 1278363728;
943 91           head->variant= variant;
944 91           head->num_buckets= bucket_count;
945 91           head->state_ofs= header_rlen;
946 91           head->table_ofs= head->state_ofs + state_rlen;
947 91           head->key_flags_ofs= head->table_ofs + table_rlen;
948 91           head->val_flags_ofs= head->key_flags_ofs + key_flags_rlen;
949 91           head->str_buf_ofs= head->val_flags_ofs + val_flags_rlen;
950              
951 91 100         if (val_is_utf8_generic >= 0)
952 67           head->general_flags |= (MPH_VALS_ARE_SAME_UTF8NESS_FLAG_BIT | (val_is_utf8_generic << MPH_VALS_ARE_SAME_UTF8NESS_SHIFT));
953 91 100         if (key_is_utf8_generic >= 0)
954 67           head->general_flags |= (MPH_KEYS_ARE_SAME_UTF8NESS_FLAG_BIT | (key_is_utf8_generic << MPH_KEYS_ARE_SAME_UTF8NESS_SHIFT));
955              
956 91           state= start + head->state_ofs;
957 91           table= (struct mph_bucket *)(start + head->table_ofs);
958 91           key_flags= start + head->key_flags_ofs;
959 91           val_flags= start + head->val_flags_ofs;
960 91           str_buf_start= start + head->str_buf_ofs;
961 91           str_buf_end= start + total_size;
962 91           str_buf_pos= str_buf_start + 2;
963              
964 91           Copy(state_pv,state,state_len,char);
965 91 50         pv= SvPV(comment_sv,pv_len);
966 91           Copy(pv,str_buf_pos,pv_len,char);
967 91           str_buf_pos += pv_len + 1; /* +1 to add a trailing null */
968              
969 301764 100         for (i= 0; i < bucket_count; i++) {
970 301673           SV **got= av_fetch(buckets_av,i,0);
971 301673           HV *hv= (HV *)SvRV(*got);
972 301673           HE *key_normalized_he= hv_fetch_ent_with_keysv(hv,MPH_KEYSV_KEY_NORMALIZED,0);
973 301673           HE *val_normalized_he= hv_fetch_ent_with_keysv(hv,MPH_KEYSV_VAL_NORMALIZED,0);
974 301673           HE *xor_val_he= hv_fetch_ent_with_keysv(hv,MPH_KEYSV_XOR_VAL,0);
975              
976 301673 100         if (xor_val_he) {
977 190698 50         table[i].xor_val= SvUV(HeVAL(xor_val_he));
978             } else {
979 110975           table[i].xor_val= 0;
980             }
981 301673 50         SETOFS(i,key_normalized_he,table,key_ofs,key_len,str_buf_start,str_buf_pos,str_buf_end,str_ofs_hv);
    50          
    0          
    0          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    50          
982 301673 50         SETOFS(i,val_normalized_he,table,val_ofs,val_len,str_buf_start,str_buf_pos,str_buf_end,str_ofs_hv);
    50          
    0          
    0          
    50          
    50          
    50          
    50          
    100          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
983 301673 100         if ( key_is_utf8_generic < 0) {
984 474           HE *key_is_utf8_he= hv_fetch_ent_with_keysv(hv,MPH_KEYSV_KEY_IS_UTF8,0);
985 474 50         if (key_is_utf8_he) {
986 474 50         UV u= SvUV(HeVAL(key_is_utf8_he));
987 474           SETBITS(u,key_flags,i,2);
988             } else {
989 0           croak("panic: out of memory? no key_is_utf8_he for %u",i);
990             }
991             }
992 301673 100         if ( val_is_utf8_generic < 0 ) {
993 474           HE *val_is_utf8_he= hv_fetch_ent_with_keysv(hv,MPH_KEYSV_VAL_IS_UTF8,0);
994 474 50         if (val_is_utf8_he) {
995 474 50         UV u= SvUV(HeVAL(val_is_utf8_he));
996 474           SETBITS(u,val_flags,i,1);
997             } else {
998 0           croak("panic: out of memory? no val_is_utf8_he for %u",i);
999             }
1000             }
1001             }
1002 91           *str_buf_pos = 0; str_buf_pos++;
1003 91           *str_buf_pos = 128; str_buf_pos++;
1004             {
1005 91           U32 r= (str_buf_pos - start) % alignment;
1006 91 100         if (r) {
1007 88           str_buf_pos += (alignment - r);
1008             }
1009             }
1010 91           *((U64 *)str_buf_pos)= mph_hash_with_state(state, start, str_buf_pos - start);
1011 91           str_buf_pos += sizeof(U64);
1012              
1013 91           SvCUR_set(sv_buf, str_buf_pos - start);
1014 91           SvPOK_on(sv_buf);
1015 91           RETVAL= sv_buf;
1016             }
1017             OUTPUT:
1018             RETVAL
1019              
1020             SV*
1021             mount_file(file_sv,error_sv,flags)
1022             SV* file_sv
1023             SV* error_sv
1024             U32 flags
1025             PROTOTYPE: $$$
1026             CODE:
1027             {
1028             struct mph_obj obj;
1029             STRLEN file_len;
1030 366 50         char *file_pv= SvPV(file_sv,file_len);
1031 366           IV mmap_status= mph_mmap(aTHX_ file_pv, &obj, error_sv, flags);
1032 366 100         if (mmap_status < 0) {
1033 185           XSRETURN_UNDEF;
1034             }
1035             /* copy obj into a new SV which we can return */
1036 181           RETVAL= newSVpvn((char *)&obj,sizeof(struct mph_obj));
1037 181           SvPOK_on(RETVAL);
1038 181           SvREADONLY_on(RETVAL);
1039             }
1040             OUTPUT:
1041             RETVAL
1042              
1043             void
1044             unmount_file(mount_sv)
1045             SV* mount_sv
1046             PROTOTYPE: $
1047             CODE:
1048             {
1049 181 50         struct mph_obj *obj= (struct mph_obj *)SvPV_nolen(mount_sv);
1050 181           mph_munmap(obj);
1051 181 50         SvOK_off(mount_sv);
1052             }
1053              
1054              
1055             int
1056             fetch_by_index(mount_sv,index,...)
1057             SV* mount_sv
1058             U32 index
1059             PROTOTYPE: $$;$$
1060             CODE:
1061             {
1062 905274 50         struct mph_obj *obj= (struct mph_obj *)SvPV_nolen(mount_sv);
1063 905274 50         SV* key_sv= items > 2 ? ST(2) : NULL;
1064 905274 50         SV* val_sv= items > 3 ? ST(3) : NULL;
1065 905274 50         if (items > 4)
1066 0           croak("Error: passed too many arguments to "
1067             "Tie::Hash::MinPerfHashTwoLevel::OnDisk::fetch_by_index(mount_sv, index, key_sv, val_sv)");
1068 905274           RETVAL= lookup_bucket(aTHX_ obj->header,index,key_sv,val_sv);
1069             }
1070             OUTPUT:
1071             RETVAL
1072              
1073             int
1074             fetch_by_key(mount_sv,key_sv,...)
1075             SV* mount_sv
1076             SV* key_sv
1077             PROTOTYPE: $$;$
1078             CODE:
1079             {
1080 1206672 50         SV* val_sv= items > 2 ? ST(2) : NULL;
1081 1206672 50         struct mph_obj *obj= (struct mph_obj *)SvPV_nolen(mount_sv);
1082 1206672 50         if (items > 3)
1083 0           croak("Error: passed too many arguments to "
1084             "Tie::Hash::MinPerfHashTwoLevel::OnDisk::fetch_by_key(mount_sv, index, key_sv)");
1085 1206672           RETVAL= lookup_key(aTHX_ obj->header,key_sv,val_sv);
1086             }
1087             OUTPUT:
1088             RETVAL
1089              
1090              
1091             SV *
1092             get_comment(self_hv)
1093             HV* self_hv
1094             ALIAS:
1095             get_hdr_magic_num = 1
1096             get_hdr_variant = 2
1097             get_hdr_num_buckets = 3
1098             get_hdr_state_ofs = 4
1099             get_hdr_table_ofs = 5
1100             get_hdr_key_flags_ofs = 6
1101             get_hdr_val_flags_ofs = 7
1102             get_hdr_str_buf_ofs = 8
1103             get_hdr_table_checksum = 9
1104             get_hdr_str_buf_checksum = 10
1105             get_state = 11
1106             PREINIT:
1107             dMY_CXT;
1108             PROTOTYPE: $
1109             CODE:
1110             {
1111             struct mph_obj *obj;
1112             SV *mount_sv;
1113             char *start;
1114 1440           HE *got= hv_fetch_ent_with_keysv(self_hv,MPH_KEYSV_MOUNT,0);
1115 1440 50         if (!got)
1116 0           croak("must be mounted to use this function");
1117 1440           mount_sv= HeVAL(got);
1118 1440 50         if (!mount_sv || !SvPOK(mount_sv))
    50          
1119 0           croak("$self->'mount' is expected to be a string!");
1120 1440 50         obj= (struct mph_obj *)SvPV_nolen(mount_sv);
1121 1440           start= (char *)obj->header;
1122 1440           switch(ix) {
1123 180           case 0: RETVAL= newSVpv(start + obj->header->str_buf_ofs + 2,0); break;
1124 0           case 1: RETVAL= newSVuv(obj->header->magic_num); break;
1125 270           case 2: RETVAL= newSVuv(obj->header->variant); break;
1126 270           case 3: RETVAL= newSVuv(obj->header->num_buckets); break;
1127 90           case 4: RETVAL= newSVuv(obj->header->state_ofs); break;
1128 90           case 5: RETVAL= newSVuv(obj->header->table_ofs); break;
1129 90           case 6: RETVAL= newSVuv(obj->header->key_flags_ofs); break;
1130 90           case 7: RETVAL= newSVuv(obj->header->val_flags_ofs); break;
1131 90           case 8: RETVAL= newSVuv(obj->header->str_buf_ofs); break;
1132 90           case 9: RETVAL= newSVuv(obj->header->table_checksum); break;
1133 90           case 10: RETVAL= newSVuv(obj->header->str_buf_checksum); break;
1134 90           case 11: RETVAL= newSVpvn(start + obj->header->state_ofs, MPH_STATE_BYTES); break;
1135             }
1136             }
1137             OUTPUT:
1138             RETVAL
1139