File Coverage

xs/ss.xsi
Criterion Covered Total %
statement 254 347 73.2
branch 281 706 39.8
condition n/a
subroutine n/a
pod n/a
total 535 1053 50.8


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 72 100         EXTRACT_NEW_ARGS(_max_size, _ttl, _lru_skip);
    100          
    100          
8 72           HashMapSS* map = hashmap_ss_create(_max_size, _ttl, _lru_skip);
9 72 50         if (!map) croak("Failed to create HashMap::SS");
10 72           RETVAL = sv_setref_pv(newSV(0), class, (void*)map);
11             OUTPUT:
12             RETVAL
13              
14             void
15             DESTROY(SV* self_sv)
16             CODE:
17 74 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
18 74           hashmap_ss_destroy(self);
19 74           sv_setiv(SvRV(self_sv), 0);
20              
21             bool
22             put(SV* self_sv, SV* key_sv, SV* value)
23             CODE:
24 82368 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
25 82368 50         EXTRACT_STR_KEY(key_sv);
26 82368 50         EXTRACT_STR_VAL(value);
27 82368           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 50034 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
36 50034 50         EXTRACT_STR_KEY(key_sv);
37             const char* val;
38             uint32_t val_len;
39             bool val_utf8;
40 50034 100         if (!hashmap_ss_get(self, _kstr, (uint32_t)_klen, _khash, _kutf8, &val, &val_len, &val_utf8))
41 6           XSRETURN_UNDEF;
42 50028           RETVAL = newSVpvn(val, val_len);
43 50028 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, _kutf8, &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 80509 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
65 80509 50         EXTRACT_STR_KEY(key_sv);
66 80509           RETVAL = hashmap_ss_remove(self, _kstr, (uint32_t)_klen, _khash, _kutf8);
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, _kutf8, &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, _kutf8);
89             OUTPUT:
90             RETVAL
91              
92             size_t
93             size(SV* self_sv)
94             CODE:
95 17 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
96 17 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 5 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
128 5 50         uint32_t now = self->expires_at ? (uint32_t)time(NULL) : 0;
129 5 50         EXTEND(SP, self->size);
130             size_t i;
131 565 100         for (i = 0; i < self->capacity; i++) {
132 560 100         if (SS_NODE_LIVE(self->nodes[i]) && !HM_TTL_SKIP_EXPIRED(self, i, now)) {
    100          
    50          
    0          
    0          
133 204           uint32_t klen = HM_UNPACK_LEN(self->nodes[i].key_len);
134 204           SV* sv = newSVpvn(self->nodes[i].key, klen);
135 204 100         if (HM_UNPACK_UTF8(self->nodes[i].key_len)) SvUTF8_on(sv);
136 204 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           free((void*)self->nodes[i].value);
241 3           self->nodes[i].value = NULL;
242 3 50         if (HM_UNLIKELY(self->lru_prev)) hashmap_ss_lru_unlink(self, (uint32_t)i);
243 3           hashmap_ss_tombstone_at(self, i);
244 3           n++;
245             }
246             }
247 2 50         if (self->iter_pos >= self->capacity) self->iter_pos = 0;
248 2 50         if (self->tombstones > self->capacity / 4 ||
249 2 100         (self->size > 0 && self->tombstones > self->size))
    50          
250 0           hashmap_ss_compact(self);
251              
252             void
253             pop(SV* self_sv)
254             PPCODE:
255 1 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
256 1 50         uint32_t now = self->expires_at ? (uint32_t)time(NULL) : 0;
257 1 50         if (self->lru_prev) {
258 1 50         while (self->lru_tail != HM_LRU_NONE) {
259 1           uint32_t idx = self->lru_tail;
260 1 50         if (HM_UNLIKELY(self->expires_at && self->expires_at[idx]) && now >= self->expires_at[idx]) {
    0          
    0          
261 0           hashmap_ss_expire_at(self, idx, true); continue;
262             }
263 1 50         EXTEND(SP, 2);
264             {
265 1           SV* ksv = newSVpvn(self->nodes[idx].key, HM_UNPACK_LEN(self->nodes[idx].key_len));
266 1 50         if (HM_UNPACK_UTF8(self->nodes[idx].key_len)) SvUTF8_on(ksv);
267 1 50         mXPUSHs(ksv);
268             }
269 1 50         if (self->nodes[idx].value) {
270 1           SV* sv = newSVpvn(self->nodes[idx].value, HM_UNPACK_LEN(self->nodes[idx].val_len));
271 1 50         if (HM_UNPACK_UTF8(self->nodes[idx].val_len)) SvUTF8_on(sv);
272 1 50         mXPUSHs(sv);
273 0 0         } else { XPUSHs(&PL_sv_undef); }
274 1           free((void*)self->nodes[idx].value);
275 1           self->nodes[idx].value = NULL;
276 1           hashmap_ss_lru_unlink(self, idx);
277 1           hashmap_ss_tombstone_at(self, idx);
278 1 50         if (self->tombstones > self->capacity / 4 ||
279 1 50         (self->size > 0 && self->tombstones > self->size))
    50          
280 0           hashmap_ss_compact(self);
281 1           XSRETURN(2);
282             }
283 0           XSRETURN_EMPTY;
284             } else {
285 0 0         while (self->iter_pos < self->capacity) {
286 0           size_t i = self->iter_pos++;
287 0 0         if (SS_NODE_LIVE(self->nodes[i]) && !HM_TTL_SKIP_EXPIRED(self, i, now)) {
    0          
    0          
    0          
    0          
288 0 0         EXTEND(SP, 2);
289             {
290 0           SV* ksv = newSVpvn(self->nodes[i].key, HM_UNPACK_LEN(self->nodes[i].key_len));
291 0 0         if (HM_UNPACK_UTF8(self->nodes[i].key_len)) SvUTF8_on(ksv);
292 0 0         mXPUSHs(ksv);
293             }
294 0 0         if (self->nodes[i].value) {
295 0           SV* sv = newSVpvn(self->nodes[i].value, HM_UNPACK_LEN(self->nodes[i].val_len));
296 0 0         if (HM_UNPACK_UTF8(self->nodes[i].val_len)) SvUTF8_on(sv);
297 0 0         mXPUSHs(sv);
298 0 0         } else { XPUSHs(&PL_sv_undef); }
299 0           free((void*)self->nodes[i].value);
300 0           self->nodes[i].value = NULL;
301 0           hashmap_ss_tombstone_at(self, i);
302 0 0         if (self->tombstones > self->capacity / 4 ||
303 0 0         (self->size > 0 && self->tombstones > self->size))
    0          
304 0           hashmap_ss_compact(self);
305 0           XSRETURN(2);
306             }
307             }
308 0           self->iter_pos = 0;
309 0           XSRETURN_EMPTY;
310             }
311              
312             void
313             shift(SV* self_sv)
314             PPCODE:
315 0 0         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    0          
    0          
    0          
316 0 0         uint32_t now = self->expires_at ? (uint32_t)time(NULL) : 0;
317 0 0         if (self->lru_prev) {
318 0 0         while (self->lru_head != HM_LRU_NONE) {
319 0           uint32_t idx = self->lru_head;
320 0 0         if (HM_UNLIKELY(self->expires_at && self->expires_at[idx]) && now >= self->expires_at[idx]) {
    0          
    0          
321 0           hashmap_ss_expire_at(self, idx, true); continue;
322             }
323 0 0         EXTEND(SP, 2);
324             {
325 0           SV* ksv = newSVpvn(self->nodes[idx].key, HM_UNPACK_LEN(self->nodes[idx].key_len));
326 0 0         if (HM_UNPACK_UTF8(self->nodes[idx].key_len)) SvUTF8_on(ksv);
327 0 0         mXPUSHs(ksv);
328             }
329 0 0         if (self->nodes[idx].value) {
330 0           SV* sv = newSVpvn(self->nodes[idx].value, HM_UNPACK_LEN(self->nodes[idx].val_len));
331 0 0         if (HM_UNPACK_UTF8(self->nodes[idx].val_len)) SvUTF8_on(sv);
332 0 0         mXPUSHs(sv);
333 0 0         } else { XPUSHs(&PL_sv_undef); }
334 0           free((void*)self->nodes[idx].value);
335 0           self->nodes[idx].value = NULL;
336 0           hashmap_ss_lru_unlink(self, idx);
337 0           hashmap_ss_tombstone_at(self, idx);
338 0 0         if (self->tombstones > self->capacity / 4 ||
339 0 0         (self->size > 0 && self->tombstones > self->size))
    0          
340 0           hashmap_ss_compact(self);
341 0           XSRETURN(2);
342             }
343 0           XSRETURN_EMPTY;
344             } else {
345 0 0         if (self->iter_pos == 0) self->iter_pos = self->capacity;
346 0 0         while (self->iter_pos > 0) {
347 0           size_t i = --self->iter_pos;
348 0 0         if (SS_NODE_LIVE(self->nodes[i]) && !HM_TTL_SKIP_EXPIRED(self, i, now)) {
    0          
    0          
    0          
    0          
349 0 0         EXTEND(SP, 2);
350             {
351 0           SV* ksv = newSVpvn(self->nodes[i].key, HM_UNPACK_LEN(self->nodes[i].key_len));
352 0 0         if (HM_UNPACK_UTF8(self->nodes[i].key_len)) SvUTF8_on(ksv);
353 0 0         mXPUSHs(ksv);
354             }
355 0 0         if (self->nodes[i].value) {
356 0           SV* sv = newSVpvn(self->nodes[i].value, HM_UNPACK_LEN(self->nodes[i].val_len));
357 0 0         if (HM_UNPACK_UTF8(self->nodes[i].val_len)) SvUTF8_on(sv);
358 0 0         mXPUSHs(sv);
359 0 0         } else { XPUSHs(&PL_sv_undef); }
360 0           free((void*)self->nodes[i].value);
361 0           self->nodes[i].value = NULL;
362 0           hashmap_ss_tombstone_at(self, i);
363 0 0         if (self->tombstones > self->capacity / 4 ||
364 0 0         (self->size > 0 && self->tombstones > self->size))
    0          
365 0           hashmap_ss_compact(self);
366 0           XSRETURN(2);
367             }
368             }
369 0           XSRETURN_EMPTY;
370             }
371              
372              
373             void
374             clear(SV* self_sv)
375             CODE:
376 9 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
377 9           hashmap_ss_clear(self);
378              
379             void
380             reserve(SV* self_sv, UV count)
381             CODE:
382 0 0         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    0          
    0          
    0          
383 0 0         if (!hashmap_ss_reserve(self, (size_t)count))
384 0           croak("Failed to reserve capacity");
385              
386             void
387             purge(SV* self_sv)
388             CODE:
389 1 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
390 1           hashmap_ss_purge(self);
391              
392             SV*
393             freeze(SV* self_sv)
394             CODE:
395 2 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
396 2 50         uint32_t now = self->expires_at ? (uint32_t)time(NULL) : 0;
397 2           SV* buf = newSV(22 + self->size * 32);
398 2           SvPOK_on(buf);
399 2           SvCUR_set(buf, 0);
400             /* Header: magic(4) + version(1) + variant_id(1) + count(4) + max_size(4) + ttl(4) + lru_skip(4) */
401 2           sv_catpvn(buf, "DHMP", 4);
402 2           { uint8_t ver = 1; sv_catpvn(buf, (const char*)&ver, 1); }
403 2           { uint8_t vid = 4; sv_catpvn(buf, (const char*)&vid, 1); }
404 2           STRLEN cnt_offset = SvCUR(buf);
405 2           { uint32_t cnt = 0; sv_catpvn(buf, (const char*)&cnt, 4); }
406 2           UV n_written = 0;
407 2           { uint32_t ms = (uint32_t)self->max_size; sv_catpvn(buf, (const char*)&ms, 4); }
408 2           { uint32_t dt = self->default_ttl; sv_catpvn(buf, (const char*)&dt, 4); }
409 2           { uint32_t ls = self->lru_skip; sv_catpvn(buf, (const char*)&ls, 4); }
410             { size_t i;
411 34 100         for (i = 0; i < self->capacity; i++) {
412 32 100         if (SS_NODE_LIVE(self->nodes[i]) && !HM_TTL_SKIP_EXPIRED(self, i, now)) {
    50          
    50          
    0          
    0          
413 3           uint32_t kl = HM_UNPACK_LEN(self->nodes[i].key_len);
414 3           uint8_t ku = HM_UNPACK_UTF8(self->nodes[i].key_len) ? 1 : 0;
415 3           sv_catpvn(buf, (const char*)&kl, 4);
416 3           sv_catpvn(buf, (const char*)&ku, 1);
417 3           sv_catpvn(buf, self->nodes[i].key, kl);
418 3           uint32_t vl = HM_UNPACK_LEN(self->nodes[i].val_len);
419 3           uint8_t vu = HM_UNPACK_UTF8(self->nodes[i].val_len) ? 1 : 0;
420 3           sv_catpvn(buf, (const char*)&vl, 4);
421 3           sv_catpvn(buf, (const char*)&vu, 1);
422 3 50         if (self->nodes[i].value) sv_catpvn(buf, self->nodes[i].value, vl);
423             /* TTL: remaining seconds (0 = none) */
424 3           { uint32_t rem = 0;
425 3 50         if (self->expires_at && self->expires_at[i] && self->expires_at[i] > now)
    0          
    0          
426 0           rem = self->expires_at[i] - now;
427 3           sv_catpvn(buf, (const char*)&rem, 4);
428             }
429 3           n_written++;
430             }
431             }
432             }
433 2           { uint32_t actual = (uint32_t)n_written;
434 2           memcpy(SvPVX(buf) + cnt_offset, &actual, 4); }
435 2           RETVAL = buf;
436             OUTPUT:
437             RETVAL
438              
439             SV*
440             thaw(char* class, SV* data)
441             CODE:
442             STRLEN dlen;
443 1           const uint8_t* p = (const uint8_t*)SvPV(data, dlen);
444 1           const uint8_t* end = p + dlen;
445 1 50         if (dlen < 22 || memcmp(p, "DHMP", 4) != 0) croak("Invalid freeze data");
    50          
446 1           p += 4;
447 1 50         uint8_t ver = *p++; if (ver != 1) croak("Unsupported freeze version %d", ver);
448 1 50         uint8_t vid = *p++; if (vid != 4) croak("Variant mismatch: expected 4, got %d", vid);
449 1           uint32_t cnt; memcpy(&cnt, p, 4); p += 4;
450 1           uint32_t ms; memcpy(&ms, p, 4); p += 4;
451 1           uint32_t dt; memcpy(&dt, p, 4); p += 4;
452 1           uint32_t ls; memcpy(&ls, p, 4); p += 4;
453 1           HashMapSS* map = hashmap_ss_create((size_t)ms, dt, ls);
454 1 50         if (!map) croak("Failed to create map for thaw");
455 1 50         if (cnt > 0) hashmap_ss_reserve(map, (size_t)cnt);
456             { uint32_t j;
457 3 100         for (j = 0; j < cnt; j++) {
458 2 50         if (p + 14 > end) { hashmap_ss_destroy(map); croak("Truncated freeze data"); }
459 2           uint32_t kl; memcpy(&kl, p, 4); p += 4;
460 2           uint8_t ku = *p++; bool kutf8 = ku ? true : false;
461 2 50         if (p + kl + 5 > end) { hashmap_ss_destroy(map); croak("Truncated freeze data"); }
462 2           const char* kstr = (const char*)p; p += kl;
463 2           uint32_t khash = hm_hash_string(kstr, kl);
464 2           uint32_t vl; memcpy(&vl, p, 4); p += 4;
465 2           uint8_t vu = *p++; bool vutf8 = vu ? true : false;
466 2 50         if (p + vl + 4 > end) { hashmap_ss_destroy(map); croak("Truncated freeze data"); }
467 2           const char* vstr = (const char*)p; p += vl;
468 2           uint32_t ttl; memcpy(&ttl, p, 4); p += 4;
469 2           hashmap_ss_put(map, kstr, kl, khash, kutf8, vstr, vl, vutf8, ttl);
470             }
471             }
472 1 50         if (p > end) croak("Truncated freeze data");
473 1           RETVAL = sv_setref_pv(newSV(0), class, (void*)map);
474             OUTPUT:
475             RETVAL
476              
477              
478             UV
479             capacity(SV* self_sv)
480             CODE:
481 0 0         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    0          
    0          
    0          
482 0 0         RETVAL = (UV)self->capacity;
483             OUTPUT:
484             RETVAL
485              
486             bool
487             persist(SV* self_sv, SV* key_sv)
488             CODE:
489 1 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
490 1 50         EXTRACT_STR_KEY(key_sv);
491 1           RETVAL = hashmap_ss_persist(self, _kstr, (uint32_t)_klen, _khash, _kutf8);
492             OUTPUT:
493             RETVAL
494              
495             SV*
496             swap(SV* self_sv, SV* key_sv, SV* new_sv)
497             CODE:
498 2 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
499 2 50         EXTRACT_STR_KEY(key_sv);
500 2           STRLEN _nvl; const char* _nvs = SvPV(new_sv, _nvl); bool _nvu = SvUTF8(new_sv) ? true : false;
501             const char* old; uint32_t old_len; bool old_utf8;
502 2 100         if (!hashmap_ss_swap(self, _kstr, (uint32_t)_klen, _khash, _kutf8, _nvs, (uint32_t)_nvl, _nvu, &old, &old_len, &old_utf8)) XSRETURN_UNDEF;
503 1           RETVAL = newSVpvn(old, old_len);
504 1 50         if (old_utf8) SvUTF8_on(RETVAL);
505 1           free((void*)old);
506             OUTPUT:
507             RETVAL
508              
509              
510             SV*
511             clone(SV* self_sv)
512             CODE:
513 1 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
514 1           HashMapSS* clone = hashmap_ss_clone(self);
515 1 50         if (!clone) croak("Failed to clone");
516 1 50         RETVAL = sv_setref_pv(newSV(0), HvNAME(SvSTASH(SvRV(self_sv))), (void*)clone);
    50          
    50          
    0          
    50          
    50          
517             OUTPUT:
518             RETVAL
519              
520             void
521             from_hash(SV* self_sv, SV* href)
522             CODE:
523 1 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
524 1 50         if (!SvROK(href) || SvTYPE(SvRV(href)) != SVt_PVHV)
    50          
525 0           croak("from_hash requires a hashref");
526 1           HV* hv = (HV*)SvRV(href);
527 1 50         hashmap_ss_reserve(self, (size_t)HvUSEDKEYS(hv));
528 1           hv_iterinit(hv);
529             HE* he;
530 3 100         while ((he = hv_iternext(hv))) {
531 2 50         STRLEN klen; const char* kstr = HePV(he, klen);
532 2 50         bool kutf8 = HeUTF8(he) ? true : false;
533 2           uint32_t khash = hm_hash_string(kstr, (uint32_t)klen);
534 2           SV* val = HeVAL(he);
535 2           STRLEN vlen; const char* vstr = SvPV(val, vlen); bool vutf8 = SvUTF8(val) ? true : false;
536 2           hashmap_ss_put(self, kstr, (uint32_t)klen, khash, kutf8, vstr, (uint32_t)vlen, vutf8, 0);
537             }
538              
539             void
540             merge(SV* self_sv, SV* other_sv)
541             CODE:
542 1 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
543 1 50         if (!SvROK(other_sv) || !SvOBJECT(SvRV(other_sv)) || SvSTASH(SvRV(other_sv)) != stash_ss)
    50          
    50          
544 0           croak("Expected a Data::HashMap::SS object");
545 1           HashMapSS* other = INT2PTR(HashMapSS*, SvIV(SvRV(other_sv)));
546 1           hashmap_ss_reserve(self, self->size + other->size);
547 1 50         uint32_t now = other->expires_at ? (uint32_t)time(NULL) : 0;
548             size_t i;
549 17 100         for (i = 0; i < other->capacity; i++) {
550 16 100         if (SS_NODE_LIVE(other->nodes[i]) && !HM_TTL_SKIP_EXPIRED(other, i, now))
    50          
    50          
    0          
    0          
551 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);
552             }
553              
554              
555             SV*
556             to_hash(SV* self_sv)
557             CODE:
558 6 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
559 6           HV* hv = newHV();
560 6 50         uint32_t now = self->expires_at ? (uint32_t)time(NULL) : 0;
561             size_t i;
562 614 100         for (i = 0; i < self->capacity; i++) {
563 608 100         if (SS_NODE_LIVE(self->nodes[i]) && !HM_TTL_SKIP_EXPIRED(self, i, now)) {
    100          
    50          
    0          
    0          
564 241           uint32_t klen = HM_UNPACK_LEN(self->nodes[i].key_len);
565 241           bool kutf8 = HM_UNPACK_UTF8(self->nodes[i].key_len);
566 241           uint32_t vlen = HM_UNPACK_LEN(self->nodes[i].val_len);
567 241           bool vutf8 = HM_UNPACK_UTF8(self->nodes[i].val_len);
568 482           SV* val = self->nodes[i].value
569 241           ? newSVpvn(self->nodes[i].value, vlen)
570 241 50         : newSV(0);
571 241 50         if (self->nodes[i].value && vutf8) SvUTF8_on(val);
    100          
572 241 100         (void)hv_store(hv, self->nodes[i].key, kutf8 ? -(I32)klen : (I32)klen, val, 0);
573             }
574             }
575 6           RETVAL = newRV_noinc((SV*)hv);
576             OUTPUT:
577             RETVAL
578              
579             bool
580             put_ttl(SV* self_sv, SV* key_sv, SV* value, UV ttl)
581             CODE:
582 102 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
583 102 50         EXTRACT_STR_KEY(key_sv);
584 102 50         EXTRACT_STR_VAL(value);
585 102           RETVAL = hashmap_ss_put(self, _kstr, (uint32_t)_klen, _khash, _kutf8,
586             _vstr, (uint32_t)_vlen, _vutf8, (uint32_t)ttl);
587             OUTPUT:
588             RETVAL
589              
590             SV*
591             get_or_set(SV* self_sv, SV* key_sv, SV* default_sv)
592             CODE:
593 9 50         EXTRACT_MAP(HashMapSS, stash_ss, "Data::HashMap::SS", self_sv);
    50          
    50          
    50          
594 9 50         EXTRACT_STR_KEY(key_sv);
595 9 50         EXTRACT_STR_VAL(default_sv);
596             bool was_found;
597 9           size_t idx = hashmap_ss_get_or_set(self, _kstr, (uint32_t)_klen, _khash, _kutf8,
598             _vstr, (uint32_t)_vlen, _vutf8, 0, &was_found);
599             (void)was_found;
600 9 50         if (idx >= self->capacity) XSRETURN_UNDEF;
601 9 50         if (self->nodes[idx].value) {
602 9           RETVAL = newSVpvn(self->nodes[idx].value, HM_UNPACK_LEN(self->nodes[idx].val_len));
603 9 50         if (HM_UNPACK_UTF8(self->nodes[idx].val_len)) SvUTF8_on(RETVAL);
604             } else {
605 0           RETVAL = newSV(0);
606             }
607             OUTPUT:
608             RETVAL
609              
610              
611