File Coverage

MinPerfHashTwoLevel.xs
Criterion Covered Total %
statement 464 530 87.5
branch 251 428 58.6
condition n/a
subroutine n/a
pod n/a
total 715 958 74.6


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