File Coverage

xs/i32s.xsi
Criterion Covered Total %
statement 106 308 34.4
branch 147 662 22.2
condition n/a
subroutine n/a
pod n/a
total 253 970 26.0


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