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