File Coverage

xs/ss.xsi
Criterion Covered Total %
statement 251 329 76.2
branch 297 718 41.3
condition n/a
subroutine n/a
pod n/a
total 548 1047 52.3


line stmt bran cond sub pod time code
1             MODULE = Data::HashMap PACKAGE = Data::HashMap::SS
2             PROTOTYPES: DISABLE
3              
4             SV*
5             new(char* class, ...)
6             CODE:
7 76 100         EXTRACT_NEW_ARGS(_max_size, _ttl, _lru_skip);
    100          
    100          
8 76           HashMapSS* map = hashmap_ss_create(_max_size, _ttl, _lru_skip);
9 76 50         if (!map) croak("Failed to create HashMap::SS");
10 76           RETVAL = sv_setref_pv(newSV(0), class, (void*)map);
11             OUTPUT:
12             RETVAL
13              
14             void
15             DESTROY(SV* self_sv)
16             CODE:
17 79 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
18 79           hashmap_ss_destroy(self);
19 79           sv_setiv(SvRV(self_sv), 0);
20              
21             bool
22             put(SV* self_sv, SV* key_sv, SV* value)
23             CODE:
24 82376 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
25 82376 50         EXTRACT_STR_KEY(key_sv);
26 82376 50         EXTRACT_STR_VAL(value);
27 82376           RETVAL = hashmap_ss_put(self, _kstr, (uint32_t)_klen, _khash, _kutf8,
28             _vstr, (uint32_t)_vlen, _vutf8, 0);
29             OUTPUT:
30             RETVAL
31              
32             SV*
33             get(SV* self_sv, SV* key_sv)
34             CODE:
35 50041 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
36 50041 50         EXTRACT_STR_KEY(key_sv);
37             const char* val;
38             uint32_t val_len;
39             bool val_utf8;
40 50041 100         if (!hashmap_ss_get(self, _kstr, (uint32_t)_klen, _khash, &val, &val_len, &val_utf8))
41 7           XSRETURN_UNDEF;
42 50034           RETVAL = newSVpvn(val, val_len);
43 50034 100         if (val_utf8) SvUTF8_on(RETVAL);
44             OUTPUT:
45             RETVAL
46              
47             SV*
48             get_direct(SV* self_sv, SV* key_sv)
49             CODE:
50 5 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
51 5 50         EXTRACT_STR_KEY(key_sv);
52             const char* val;
53             uint32_t val_len;
54             bool val_utf8;
55 5 100         if (!hashmap_ss_get(self, _kstr, (uint32_t)_klen, _khash, &val, &val_len, &val_utf8))
56 1           XSRETURN_UNDEF;
57 4           RETVAL = hm_zerocopy_sv(aTHX_ val, val_len, val_utf8);
58             OUTPUT:
59             RETVAL
60              
61             bool
62             remove(SV* self_sv, SV* key_sv)
63             CODE:
64 80510 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
65 80510 50         EXTRACT_STR_KEY(key_sv);
66 80510           RETVAL = hashmap_ss_remove(self, _kstr, (uint32_t)_klen, _khash);
67             OUTPUT:
68             RETVAL
69              
70             SV*
71             take(SV* self_sv, SV* key_sv)
72             CODE:
73 1 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
74 1 50         EXTRACT_STR_KEY(key_sv);
75             const char* val; uint32_t val_len; bool val_utf8;
76 1 50         if (!hashmap_ss_take(self, _kstr, (uint32_t)_klen, _khash, &val, &val_len, &val_utf8)) XSRETURN_UNDEF;
77 1           RETVAL = newSVpvn(val, val_len);
78 1 50         if (val_utf8) SvUTF8_on(RETVAL);
79 1           free((void*)val);
80             OUTPUT:
81             RETVAL
82              
83             bool
84             exists(SV* self_sv, SV* key_sv)
85             CODE:
86 2 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
87 2 50         EXTRACT_STR_KEY(key_sv);
88 2           RETVAL = hashmap_ss_exists(self, _kstr, (uint32_t)_klen, _khash);
89             OUTPUT:
90             RETVAL
91              
92             size_t
93             size(SV* self_sv)
94             CODE:
95 21 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
96 21 50         RETVAL = self->size;
97             OUTPUT:
98             RETVAL
99              
100             size_t
101             max_size(SV* self_sv)
102             CODE:
103 0 0         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    0          
    0          
    0          
104 0 0         RETVAL = self->max_size;
105             OUTPUT:
106             RETVAL
107              
108             UV
109             ttl(SV* self_sv)
110             CODE:
111 0 0         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    0          
    0          
    0          
112 0 0         RETVAL = (UV)self->default_ttl;
113             OUTPUT:
114             RETVAL
115              
116             UV
117             lru_skip(SV* self_sv)
118             CODE:
119 0 0         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    0          
    0          
    0          
120 0 0         RETVAL = (UV)self->lru_skip;
121             OUTPUT:
122             RETVAL
123              
124             void
125             keys(SV* self_sv)
126             PPCODE:
127 6 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
128 6 50         uint32_t now = self->expires_at ? (uint32_t)time(NULL) : 0;
129 6 50         EXTEND(SP, self->size);
130             size_t i;
131 582 100         for (i = 0; i < self->capacity; i++) {
132 576 100         if (SS_NODE_LIVE(self->nodes[i]) && !HM_TTL_SKIP_EXPIRED(self, i, now)) {
    100          
    50          
    0          
    0          
133 205           uint32_t klen = HM_UNPACK_LEN(self->nodes[i].key_len);
134 205           SV* sv = newSVpvn(self->nodes[i].key, klen);
135 205 100         if (HM_UNPACK_UTF8(self->nodes[i].key_len)) SvUTF8_on(sv);
136 205 50         mXPUSHs(sv);
137             }
138             }
139              
140             void
141             values(SV* self_sv)
142             PPCODE:
143 3 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
144 3 50         uint32_t now = self->expires_at ? (uint32_t)time(NULL) : 0;
145 3 50         EXTEND(SP, self->size);
146             size_t i;
147 531 100         for (i = 0; i < self->capacity; i++) {
148 528 100         if (SS_NODE_LIVE(self->nodes[i]) && !HM_TTL_SKIP_EXPIRED(self, i, now)) {
    100          
    50          
    0          
    0          
149 200 50         if (self->nodes[i].value) {
150 200           SV* sv = newSVpvn(self->nodes[i].value,
151             HM_UNPACK_LEN(self->nodes[i].val_len));
152 200 50         if (HM_UNPACK_UTF8(self->nodes[i].val_len)) SvUTF8_on(sv);
153 200 50         mXPUSHs(sv);
154             } else {
155 0 0         XPUSHs(&PL_sv_undef);
156             }
157             }
158             }
159              
160             void
161             items(SV* self_sv)
162             PPCODE:
163 1 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
164 1 50         uint32_t now = self->expires_at ? (uint32_t)time(NULL) : 0;
165 1 50         EXTEND(SP, self->size * 2);
166             size_t i;
167 17 100         for (i = 0; i < self->capacity; i++) {
168 16 100         if (SS_NODE_LIVE(self->nodes[i]) && !HM_TTL_SKIP_EXPIRED(self, i, now)) {
    50          
    50          
    0          
    0          
169 2           uint32_t klen = HM_UNPACK_LEN(self->nodes[i].key_len);
170 2           SV* sv = newSVpvn(self->nodes[i].key, klen);
171 2 50         if (HM_UNPACK_UTF8(self->nodes[i].key_len)) SvUTF8_on(sv);
172 2 50         mXPUSHs(sv);
173 2 50         if (self->nodes[i].value) {
174 2           SV* vsv = newSVpvn(self->nodes[i].value,
175             HM_UNPACK_LEN(self->nodes[i].val_len));
176 2 50         if (HM_UNPACK_UTF8(self->nodes[i].val_len)) SvUTF8_on(vsv);
177 2 50         mXPUSHs(vsv);
178             } else {
179 0 0         XPUSHs(&PL_sv_undef);
180             }
181             }
182             }
183              
184             void
185             each(SV* self_sv)
186             PPCODE:
187 47 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
188 47 50         uint32_t now = self->expires_at ? (uint32_t)time(NULL) : 0;
189 93 100         while (self->iter_pos < self->capacity) {
190 90           size_t i = self->iter_pos++;
191 90 100         if (SS_NODE_LIVE(self->nodes[i]) && !HM_TTL_SKIP_EXPIRED(self, i, now)) {
    50          
    50          
    0          
    0          
192 44 50         EXTEND(SP, 2);
193             {
194 44           uint32_t klen = HM_UNPACK_LEN(self->nodes[i].key_len);
195 44           SV* ksv = newSVpvn(self->nodes[i].key, klen);
196 44 100         if (HM_UNPACK_UTF8(self->nodes[i].key_len)) SvUTF8_on(ksv);
197 44 50         mXPUSHs(ksv);
198             }
199 44 100         if (GIMME_V == G_SCALAR) XSRETURN(1);
200 43 50         if (self->nodes[i].value) {
201 43           SV* vsv = newSVpvn(self->nodes[i].value,
202             HM_UNPACK_LEN(self->nodes[i].val_len));
203 43 100         if (HM_UNPACK_UTF8(self->nodes[i].val_len)) SvUTF8_on(vsv);
204 43 50         mXPUSHs(vsv);
205             } else {
206 0 0         XPUSHs(&PL_sv_undef);
207             }
208 43           XSRETURN(2);
209             }
210             }
211 3           self->iter_pos = 0;
212 3           XSRETURN_EMPTY;
213              
214             void
215             iter_reset(SV* self_sv)
216             CODE:
217 0 0         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    0          
    0          
    0          
218 0           self->iter_pos = 0;
219              
220             void
221             drain(SV* self_sv, UV count)
222             PPCODE:
223 2 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
224 2 50         uint32_t now = self->expires_at ? (uint32_t)time(NULL) : 0;
225 2           UV n = 0;
226 2 50         EXTEND(SP, (count < self->size ? count : self->size) * 2);
227 19 50         while (self->iter_pos < self->capacity && n < count) {
    100          
228 17           size_t i = self->iter_pos++;
229 17 100         if (SS_NODE_LIVE(self->nodes[i]) && !HM_TTL_SKIP_EXPIRED(self, i, now)) {
    50          
    50          
    0          
    0          
230             {
231 3           SV* ksv = newSVpvn(self->nodes[i].key, HM_UNPACK_LEN(self->nodes[i].key_len));
232 3 100         if (HM_UNPACK_UTF8(self->nodes[i].key_len)) SvUTF8_on(ksv);
233 3 50         mXPUSHs(ksv);
234             }
235 3 50         if (self->nodes[i].value) {
236 3           SV* sv = newSVpvn(self->nodes[i].value, HM_UNPACK_LEN(self->nodes[i].val_len));
237 3 50         if (HM_UNPACK_UTF8(self->nodes[i].val_len)) SvUTF8_on(sv);
238 3 50         mXPUSHs(sv);
239 0 0         } else { XPUSHs(&PL_sv_undef); }
240 3 50         if (HM_UNLIKELY(self->lru_prev)) hashmap_ss_lru_unlink(self, (uint32_t)i);
241 3           hashmap_ss_tombstone_at(self, i);
242 3           n++;
243             }
244             }
245 2 50         if (self->iter_pos >= self->capacity) self->iter_pos = 0;
246 2 50         HM_MAYBE_COMPACT_XS(self, hashmap_ss_compact);
    100          
    50          
247              
248             void
249             pop(SV* self_sv)
250             PPCODE:
251 1 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
252 1 50         uint32_t now = self->expires_at ? (uint32_t)time(NULL) : 0;
253 1 50         if (self->lru_prev) {
254 1 50         while (self->lru_tail != HM_LRU_NONE) {
255 1           uint32_t idx = self->lru_tail;
256 1 50         if (HM_UNLIKELY(HM_TTL_SKIP_EXPIRED(self, idx, now))) {
    0          
    50          
    0          
257 0           hashmap_ss_expire_at(self, idx, true); continue;
258             }
259 1 50         EXTEND(SP, 2);
260             {
261 1           SV* ksv = newSVpvn(self->nodes[idx].key, HM_UNPACK_LEN(self->nodes[idx].key_len));
262 1 50         if (HM_UNPACK_UTF8(self->nodes[idx].key_len)) SvUTF8_on(ksv);
263 1 50         mXPUSHs(ksv);
264             }
265 1 50         if (self->nodes[idx].value) {
266 1           SV* sv = newSVpvn(self->nodes[idx].value, HM_UNPACK_LEN(self->nodes[idx].val_len));
267 1 50         if (HM_UNPACK_UTF8(self->nodes[idx].val_len)) SvUTF8_on(sv);
268 1 50         mXPUSHs(sv);
269 0 0         } else { XPUSHs(&PL_sv_undef); }
270 1           hashmap_ss_lru_unlink(self, idx);
271 1           hashmap_ss_tombstone_at(self, idx);
272 1 50         HM_MAYBE_COMPACT_XS(self, hashmap_ss_compact);
    50          
    50          
273 1           XSRETURN(2);
274             }
275 0           XSRETURN_EMPTY;
276             } else {
277 0 0         while (self->iter_pos < self->capacity) {
278 0           size_t i = self->iter_pos++;
279 0 0         if (SS_NODE_LIVE(self->nodes[i]) && !HM_TTL_SKIP_EXPIRED(self, i, now)) {
    0          
    0          
    0          
    0          
280 0 0         EXTEND(SP, 2);
281             {
282 0           SV* ksv = newSVpvn(self->nodes[i].key, HM_UNPACK_LEN(self->nodes[i].key_len));
283 0 0         if (HM_UNPACK_UTF8(self->nodes[i].key_len)) SvUTF8_on(ksv);
284 0 0         mXPUSHs(ksv);
285             }
286 0 0         if (self->nodes[i].value) {
287 0           SV* sv = newSVpvn(self->nodes[i].value, HM_UNPACK_LEN(self->nodes[i].val_len));
288 0 0         if (HM_UNPACK_UTF8(self->nodes[i].val_len)) SvUTF8_on(sv);
289 0 0         mXPUSHs(sv);
290 0 0         } else { XPUSHs(&PL_sv_undef); }
291 0           hashmap_ss_tombstone_at(self, i);
292 0 0         HM_MAYBE_COMPACT_XS(self, hashmap_ss_compact);
    0          
    0          
293 0           XSRETURN(2);
294             }
295             }
296 0           self->iter_pos = 0;
297 0           XSRETURN_EMPTY;
298             }
299              
300             void
301             shift(SV* self_sv)
302             PPCODE:
303 0 0         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    0          
    0          
    0          
304 0 0         uint32_t now = self->expires_at ? (uint32_t)time(NULL) : 0;
305 0 0         if (self->lru_prev) {
306 0 0         while (self->lru_head != HM_LRU_NONE) {
307 0           uint32_t idx = self->lru_head;
308 0 0         if (HM_UNLIKELY(HM_TTL_SKIP_EXPIRED(self, idx, now))) {
    0          
    0          
    0          
309 0           hashmap_ss_expire_at(self, idx, true); continue;
310             }
311 0 0         EXTEND(SP, 2);
312             {
313 0           SV* ksv = newSVpvn(self->nodes[idx].key, HM_UNPACK_LEN(self->nodes[idx].key_len));
314 0 0         if (HM_UNPACK_UTF8(self->nodes[idx].key_len)) SvUTF8_on(ksv);
315 0 0         mXPUSHs(ksv);
316             }
317 0 0         if (self->nodes[idx].value) {
318 0           SV* sv = newSVpvn(self->nodes[idx].value, HM_UNPACK_LEN(self->nodes[idx].val_len));
319 0 0         if (HM_UNPACK_UTF8(self->nodes[idx].val_len)) SvUTF8_on(sv);
320 0 0         mXPUSHs(sv);
321 0 0         } else { XPUSHs(&PL_sv_undef); }
322 0           hashmap_ss_lru_unlink(self, idx);
323 0           hashmap_ss_tombstone_at(self, idx);
324 0 0         HM_MAYBE_COMPACT_XS(self, hashmap_ss_compact);
    0          
    0          
325 0           XSRETURN(2);
326             }
327 0           XSRETURN_EMPTY;
328             } else {
329 0 0         if (self->iter_pos == 0) self->iter_pos = self->capacity;
330 0 0         while (self->iter_pos > 0) {
331 0           size_t i = --self->iter_pos;
332 0 0         if (SS_NODE_LIVE(self->nodes[i]) && !HM_TTL_SKIP_EXPIRED(self, i, now)) {
    0          
    0          
    0          
    0          
333 0 0         EXTEND(SP, 2);
334             {
335 0           SV* ksv = newSVpvn(self->nodes[i].key, HM_UNPACK_LEN(self->nodes[i].key_len));
336 0 0         if (HM_UNPACK_UTF8(self->nodes[i].key_len)) SvUTF8_on(ksv);
337 0 0         mXPUSHs(ksv);
338             }
339 0 0         if (self->nodes[i].value) {
340 0           SV* sv = newSVpvn(self->nodes[i].value, HM_UNPACK_LEN(self->nodes[i].val_len));
341 0 0         if (HM_UNPACK_UTF8(self->nodes[i].val_len)) SvUTF8_on(sv);
342 0 0         mXPUSHs(sv);
343 0 0         } else { XPUSHs(&PL_sv_undef); }
344 0           hashmap_ss_tombstone_at(self, i);
345 0 0         HM_MAYBE_COMPACT_XS(self, hashmap_ss_compact);
    0          
    0          
346 0           XSRETURN(2);
347             }
348             }
349 0           XSRETURN_EMPTY;
350             }
351              
352              
353             void
354             clear(SV* self_sv)
355             CODE:
356 9 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
357 9           hashmap_ss_clear(self);
358              
359             void
360             reserve(SV* self_sv, UV count)
361             CODE:
362 0 0         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    0          
    0          
    0          
363 0 0         if (!hashmap_ss_reserve(self, (size_t)count))
364 0           croak("Failed to reserve capacity");
365              
366             void
367             purge(SV* self_sv)
368             CODE:
369 1 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
370 1           hashmap_ss_purge(self);
371              
372             SV*
373             freeze(SV* self_sv)
374             CODE:
375 3 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
376 3 100         uint32_t now = self->expires_at ? (uint32_t)time(NULL) : 0;
377 3           SV* buf = newSV(22 + self->size * 32);
378 3           SvPOK_on(buf);
379 3           SvCUR_set(buf, 0);
380             /* Header: magic(4) + version(1) + variant_id(1) + count(4) + max_size(4) + ttl(4) + lru_skip(4) */
381 3           sv_catpvn(buf, "DHMP", 4);
382 3           { uint8_t ver = 1; sv_catpvn(buf, (const char*)&ver, 1); }
383 3           { uint8_t vid = 4; sv_catpvn(buf, (const char*)&vid, 1); }
384 3           STRLEN cnt_offset = SvCUR(buf);
385 3           { uint32_t cnt = 0; sv_catpvn(buf, (const char*)&cnt, 4); }
386 3           UV n_written = 0;
387 3           { uint32_t ms = (uint32_t)self->max_size; sv_catpvn(buf, (const char*)&ms, 4); }
388 3           { uint32_t dt = self->default_ttl; sv_catpvn(buf, (const char*)&dt, 4); }
389 3           { uint32_t ls = self->lru_skip; sv_catpvn(buf, (const char*)&ls, 4); }
390             { size_t i;
391 51 100         for (i = 0; i < self->capacity; i++) {
392 48 100         if (SS_NODE_LIVE(self->nodes[i]) && !HM_TTL_SKIP_EXPIRED(self, i, now)) {
    50          
    100          
    100          
    50          
393 5           uint32_t kl = HM_UNPACK_LEN(self->nodes[i].key_len);
394 5           uint8_t ku = HM_UNPACK_UTF8(self->nodes[i].key_len) ? 1 : 0;
395 5           sv_catpvn(buf, (const char*)&kl, 4);
396 5           sv_catpvn(buf, (const char*)&ku, 1);
397 5           sv_catpvn(buf, self->nodes[i].key, kl);
398 5           uint32_t vl = HM_UNPACK_LEN(self->nodes[i].val_len);
399 5           uint8_t vu = HM_UNPACK_UTF8(self->nodes[i].val_len) ? 1 : 0;
400 5           sv_catpvn(buf, (const char*)&vl, 4);
401 5           sv_catpvn(buf, (const char*)&vu, 1);
402 5 50         if (self->nodes[i].value) sv_catpvn(buf, self->nodes[i].value, vl);
403             /* TTL: remaining seconds (0 = none) */
404 5           { uint32_t rem = 0;
405 5 100         if (self->expires_at && self->expires_at[i] && self->expires_at[i] >= now) {
    100          
    50          
406 1           rem = self->expires_at[i] - now;
407 1 50         if (!rem) rem = 1;
408             }
409 5           sv_catpvn(buf, (const char*)&rem, 4);
410             }
411 5           n_written++;
412             }
413             }
414             }
415 3           { uint32_t actual = (uint32_t)n_written;
416 3           memcpy(SvPVX(buf) + cnt_offset, &actual, 4); }
417 3           RETVAL = buf;
418             OUTPUT:
419             RETVAL
420              
421             SV*
422             thaw(char* class, SV* data)
423             CODE:
424             STRLEN dlen;
425 2           const uint8_t* p = (const uint8_t*)SvPV(data, dlen);
426 2           const uint8_t* end = p + dlen;
427 2 50         if (dlen < 22 || memcmp(p, "DHMP", 4) != 0) croak("Invalid freeze data");
    50          
428 2           p += 4;
429 2 50         uint8_t ver = *p++; if (ver != 1) croak("Unsupported freeze version %d", ver);
430 2 50         uint8_t vid = *p++; if (vid != 4) croak("Variant mismatch: expected 4, got %d", vid);
431 2           uint32_t cnt; memcpy(&cnt, p, 4); p += 4;
432 2           uint32_t ms; memcpy(&ms, p, 4); p += 4;
433 2           uint32_t dt; memcpy(&dt, p, 4); p += 4;
434 2           uint32_t ls; memcpy(&ls, p, 4); p += 4;
435 2           HashMapSS* map = hashmap_ss_create((size_t)ms, dt, ls);
436 2 50         if (!map) croak("Failed to create map for thaw");
437 2 50         if (cnt > 0) hashmap_ss_reserve(map, (size_t)cnt);
438             { uint32_t j;
439 6 100         for (j = 0; j < cnt; j++) {
440 4 50         if (p + 14 > end) { hashmap_ss_destroy(map); croak("Truncated freeze data"); }
441 4           uint32_t kl; memcpy(&kl, p, 4); p += 4;
442 4           uint8_t ku = *p++; bool kutf8 = ku ? true : false;
443 4 50         if (p + kl + 5 > end) { hashmap_ss_destroy(map); croak("Truncated freeze data"); }
444 4           const char* kstr = (const char*)p; p += kl;
445 4           uint32_t khash = hm_hash_string(kstr, kl);
446 4           uint32_t vl; memcpy(&vl, p, 4); p += 4;
447 4           uint8_t vu = *p++; bool vutf8 = vu ? true : false;
448 4 50         if (p + vl + 4 > end) { hashmap_ss_destroy(map); croak("Truncated freeze data"); }
449 4           const char* vstr = (const char*)p; p += vl;
450 4           uint32_t ttl; memcpy(&ttl, p, 4); p += 4;
451 4 50         if (!hashmap_ss_put(map, kstr, kl, khash, kutf8, vstr, vl, vutf8, ttl)) { hashmap_ss_destroy(map); croak("OOM during thaw"); }
452 4 100         if (ttl == 0 && map->expires_at) hashmap_ss_persist(map, kstr, kl, khash);
    100          
453             }
454             }
455 2 50         if (p > end) croak("Truncated freeze data");
456 2           RETVAL = sv_setref_pv(newSV(0), class, (void*)map);
457             OUTPUT:
458             RETVAL
459              
460              
461             UV
462             capacity(SV* self_sv)
463             CODE:
464 0 0         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    0          
    0          
    0          
465 0 0         RETVAL = (UV)self->capacity;
466             OUTPUT:
467             RETVAL
468              
469             bool
470             persist(SV* self_sv, SV* key_sv)
471             CODE:
472 2 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
473 2 50         EXTRACT_STR_KEY(key_sv);
474 2           RETVAL = hashmap_ss_persist(self, _kstr, (uint32_t)_klen, _khash);
475             OUTPUT:
476             RETVAL
477              
478             SV*
479             swap(SV* self_sv, SV* key_sv, SV* new_sv)
480             CODE:
481 2 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
482 2 50         EXTRACT_STR_KEY(key_sv);
483 2           STRLEN _nvl; const char* _nvs = SvPV(new_sv, _nvl); bool _nvu = SvUTF8(new_sv) ? true : false;
484             const char* old; uint32_t old_len; bool old_utf8;
485 2 100         if (!hashmap_ss_swap(self, _kstr, (uint32_t)_klen, _khash, _nvs, (uint32_t)_nvl, _nvu, &old, &old_len, &old_utf8)) XSRETURN_UNDEF;
486 1           RETVAL = newSVpvn(old, old_len);
487 1 50         if (old_utf8) SvUTF8_on(RETVAL);
488 1           free((void*)old);
489             OUTPUT:
490             RETVAL
491              
492              
493             SV*
494             clone(SV* self_sv)
495             CODE:
496 1 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
497 1           HashMapSS* clone = hashmap_ss_clone(self);
498 1 50         if (!clone) croak("Failed to clone");
499 1 50         RETVAL = sv_setref_pv(newSV(0), HvNAME(SvSTASH(SvRV(self_sv))), (void*)clone);
    50          
    50          
    0          
    50          
    50          
500             OUTPUT:
501             RETVAL
502              
503             void
504             from_hash(SV* self_sv, SV* href)
505             CODE:
506 1 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
507 1 50         if (!SvROK(href) || SvTYPE(SvRV(href)) != SVt_PVHV)
    50          
508 0           croak("from_hash requires a hashref");
509 1           HV* hv = (HV*)SvRV(href);
510 1 50         hashmap_ss_reserve(self, (size_t)HvUSEDKEYS(hv));
511 1           hv_iterinit(hv);
512             HE* he;
513 3 100         while ((he = hv_iternext(hv))) {
514 2 50         STRLEN klen; const char* kstr = HePV(he, klen);
515 2 50         bool kutf8 = HeUTF8(he) ? true : false;
516 2           uint32_t khash = hm_hash_string(kstr, (uint32_t)klen);
517 2           SV* val = HeVAL(he);
518 2           STRLEN vlen; const char* vstr = SvPV(val, vlen); bool vutf8 = SvUTF8(val) ? true : false;
519 2           hashmap_ss_put(self, kstr, (uint32_t)klen, khash, kutf8, vstr, (uint32_t)vlen, vutf8, 0);
520             }
521              
522             void
523             merge(SV* self_sv, SV* other_sv)
524             CODE:
525 1 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
526 1 50         if (!SvROK(other_sv) || !SvOBJECT(SvRV(other_sv)) || SvSTASH(SvRV(other_sv)) != stash_ss)
    50          
    50          
527 0           croak("Expected a Data::HashMap::SS object");
528 1           HashMapSS* other = INT2PTR(HashMapSS*, SvIV(SvRV(other_sv)));
529 1           hashmap_ss_reserve(self, self->size + other->size);
530 1 50         uint32_t now = other->expires_at ? (uint32_t)time(NULL) : 0;
531             size_t i;
532 17 100         for (i = 0; i < other->capacity; i++) {
533 16 100         if (SS_NODE_LIVE(other->nodes[i]) && !HM_TTL_SKIP_EXPIRED(other, i, now))
    50          
    50          
    0          
    0          
534 1           hashmap_ss_put(self, other->nodes[i].key, HM_UNPACK_LEN(other->nodes[i].key_len), other->nodes[i].key_hash, HM_UNPACK_UTF8(other->nodes[i].key_len), other->nodes[i].value, HM_UNPACK_LEN(other->nodes[i].val_len), HM_UNPACK_UTF8(other->nodes[i].val_len), 0);
535             }
536              
537              
538             SV*
539             to_hash(SV* self_sv)
540             CODE:
541 6 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
542 6           HV* hv = newHV();
543 6 50         uint32_t now = self->expires_at ? (uint32_t)time(NULL) : 0;
544             size_t i;
545 614 100         for (i = 0; i < self->capacity; i++) {
546 608 100         if (SS_NODE_LIVE(self->nodes[i]) && !HM_TTL_SKIP_EXPIRED(self, i, now)) {
    100          
    50          
    0          
    0          
547 241           uint32_t klen = HM_UNPACK_LEN(self->nodes[i].key_len);
548 241           bool kutf8 = HM_UNPACK_UTF8(self->nodes[i].key_len);
549 241           uint32_t vlen = HM_UNPACK_LEN(self->nodes[i].val_len);
550 241           bool vutf8 = HM_UNPACK_UTF8(self->nodes[i].val_len);
551 482           SV* val = self->nodes[i].value
552 241           ? newSVpvn(self->nodes[i].value, vlen)
553 241 50         : newSV(0);
554 241 50         if (self->nodes[i].value && vutf8) SvUTF8_on(val);
    100          
555 241 100         (void)hv_store(hv, self->nodes[i].key, kutf8 ? -(I32)klen : (I32)klen, val, 0);
556             }
557             }
558 6           RETVAL = newRV_noinc((SV*)hv);
559             OUTPUT:
560             RETVAL
561              
562             bool
563             put_ttl(SV* self_sv, SV* key_sv, SV* value, UV ttl)
564             CODE:
565 102 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
566 102 50         EXTRACT_STR_KEY(key_sv);
567 102 50         EXTRACT_STR_VAL(value);
568 102           RETVAL = hashmap_ss_put(self, _kstr, (uint32_t)_klen, _khash, _kutf8,
569             _vstr, (uint32_t)_vlen, _vutf8, (uint32_t)ttl);
570             OUTPUT:
571             RETVAL
572              
573             SV*
574             get_or_set(SV* self_sv, SV* key_sv, SV* default_sv)
575             CODE:
576 9 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
577 9 50         EXTRACT_STR_KEY(key_sv);
578 9 50         EXTRACT_STR_VAL(default_sv);
579             bool was_found;
580 9           size_t idx = hashmap_ss_get_or_set(self, _kstr, (uint32_t)_klen, _khash, _kutf8,
581             _vstr, (uint32_t)_vlen, _vutf8, 0, &was_found);
582 9 50         if (idx >= self->capacity) XSRETURN_UNDEF;
583 9 50         if (self->nodes[idx].value) {
584 9           RETVAL = newSVpvn(self->nodes[idx].value, HM_UNPACK_LEN(self->nodes[idx].val_len));
585 9 50         if (HM_UNPACK_UTF8(self->nodes[idx].val_len)) SvUTF8_on(RETVAL);
586             } else {
587 0           RETVAL = newSV(0);
588             }
589             OUTPUT:
590             RETVAL
591              
592              
593