File Coverage

Shared.xs
Criterion Covered Total %
statement 99 100 99.0
branch 90 148 60.8
condition n/a
subroutine n/a
pod n/a
total 189 248 76.2


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             #include "ppport.h"
6             #include "cuckoo.h"
7              
8             #define EXTRACT(sv) \
9             if (!sv_isobject(sv) || !sv_derived_from(sv, "Data::CuckooFilter::Shared")) \
10             croak("Expected a Data::CuckooFilter::Shared object"); \
11             CfHandle *h = INT2PTR(CfHandle*, SvIV(SvRV(sv))); \
12             if (!h) croak("Attempted to use a destroyed Data::CuckooFilter::Shared object")
13              
14             #define MAKE_OBJ(class, handle) \
15             SV *obj = newSViv(PTR2IV(handle)); \
16             SV *ref = newRV_noinc(obj); \
17             sv_bless(ref, gv_stashpv(class, GV_ADD)); \
18             RETVAL = ref
19              
20             MODULE = Data::CuckooFilter::Shared PACKAGE = Data::CuckooFilter::Shared
21              
22             PROTOTYPES: DISABLE
23              
24             SV *
25             new(class, path = &PL_sv_undef, capacity = 0)
26             const char *class
27             SV *path
28             UV capacity
29             PREINIT:
30             char errbuf[CF_ERR_BUFLEN];
31             CODE:
32 22 100         const char *p = SvOK(path) ? SvPV_nolen(path) : NULL;
33 22 100         if (capacity < 1)
34 1           croak("Data::CuckooFilter::Shared->new: capacity must be >= 1");
35 21           CfHandle *h = cf_create(p, (uint64_t)capacity, errbuf);
36 21 100         if (!h) croak("Data::CuckooFilter::Shared->new: %s", errbuf);
37 19           MAKE_OBJ(class, h);
38             OUTPUT:
39             RETVAL
40              
41             SV *
42             new_memfd(class, name = &PL_sv_undef, capacity = 0)
43             const char *class
44             SV *name
45             UV capacity
46             PREINIT:
47             char errbuf[CF_ERR_BUFLEN];
48             CODE:
49 3 100         const char *nm = SvOK(name) ? SvPV_nolen(name) : NULL; /* undef -> default label */
50 3 100         if (capacity < 1)
51 1           croak("Data::CuckooFilter::Shared->new_memfd: capacity must be >= 1");
52 2           CfHandle *h = cf_create_memfd(nm, (uint64_t)capacity, errbuf);
53 2 50         if (!h) croak("Data::CuckooFilter::Shared->new_memfd: %s", errbuf);
54 2           MAKE_OBJ(class, h);
55             OUTPUT:
56             RETVAL
57              
58             SV *
59             new_from_fd(class, fd)
60             const char *class
61             int fd
62             PREINIT:
63             char errbuf[CF_ERR_BUFLEN];
64             CODE:
65 2           CfHandle *h = cf_open_fd(fd, errbuf);
66 2 100         if (!h) croak("Data::CuckooFilter::Shared->new_from_fd: %s", errbuf);
67 1           MAKE_OBJ(class, h);
68             OUTPUT:
69             RETVAL
70              
71             void
72             DESTROY(self)
73             SV *self
74             CODE:
75 24 50         if (sv_isobject(self) && sv_derived_from(self, "Data::CuckooFilter::Shared")) {
    50          
76 24           CfHandle *h = INT2PTR(CfHandle*, SvIV(SvRV(self)));
77 24 100         if (h) { sv_setiv(SvRV(self), 0); cf_destroy(h); } /* null first: activates EXTRACT's use-after-destroy croak + makes a double DESTROY a no-op */
78             }
79              
80             int
81             add(self, item)
82             SV *self
83             SV *item
84             PREINIT:
85 21823 50         EXTRACT(self);
    50          
    50          
86             STRLEN n;
87             const char *s;
88             CODE:
89 21823           s = SvPVbyte(item, n); /* may croak (wide char) -- BEFORE the lock */
90 21822           cf_rwlock_wrlock(h);
91 21822           RETVAL = cf_add_locked(h, s, n);
92 21822           __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
93 21822           cf_rwlock_wrunlock(h);
94             OUTPUT:
95             RETVAL
96              
97             UV
98             add_many(self, items)
99             SV *self
100             SV *items
101             PREINIT:
102 8 50         EXTRACT(self);
    50          
    50          
103             AV *av;
104             IV top;
105 8 50         UV added = 0;
106             CODE:
107 8 100         if (!SvROK(items) || SvTYPE(SvRV(items)) != SVt_PVAV)
    100          
108 2           croak("Data::CuckooFilter::Shared->add_many: expected an array reference");
109 6           av = (AV *)SvRV(items);
110 6           top = av_len(av); /* last index, -1 if empty */
111             {
112 6 100         STRLEN cnt = (top >= 0) ? (STRLEN)(top + 1) : 0, i;
113 6           const char **ps = NULL; STRLEN *ls = NULL;
114 6 100         if (cnt) { /* resolve all bytes BEFORE locking */
115 5 50         Newx(ps, cnt, const char *); SAVEFREEPV(ps); /* freed on return OR unwind */
116 5 50         Newx(ls, cnt, STRLEN); SAVEFREEPV(ls);
117 5506 100         for (i = 0; i < cnt; i++) { /* a croak here holds NO lock; SAVEFREEPV cleans up */
118 5502           SV **el = av_fetch(av, (SSize_t)i, 0);
119 5502 50         if (el && *el) ps[i] = SvPVbyte(*el, ls[i]);
    50          
120 0           else { ps[i] = ""; ls[i] = 0; }
121             }
122             }
123 5           cf_rwlock_wrlock(h); /* locked region: NO croak-capable calls */
124 5505 100         for (i = 0; i < cnt; i++) added += (UV)cf_add_locked(h, ps[i], ls[i]);
125 5           __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED); /* a call always counts, even an empty batch */
126 5           cf_rwlock_wrunlock(h);
127             }
128 5 50         RETVAL = added;
129             OUTPUT:
130             RETVAL
131              
132             int
133             contains(self, item)
134             SV *self
135             SV *item
136             PREINIT:
137 26827 50         EXTRACT(self);
    50          
    100          
138             STRLEN n;
139             const char *s;
140             CODE:
141 26826           s = SvPVbyte(item, n); /* may croak (wide char) -- BEFORE the lock */
142 26825           cf_rwlock_rdlock(h);
143 26825           RETVAL = cf_contains_locked(h, s, n);
144 26825           cf_rwlock_rdunlock(h);
145             OUTPUT:
146             RETVAL
147              
148             int
149             remove(self, item)
150             SV *self
151             SV *item
152             PREINIT:
153 4006 50         EXTRACT(self);
    50          
    50          
154             STRLEN n;
155             const char *s;
156             CODE:
157 4006           s = SvPVbyte(item, n); /* may croak (wide char) -- BEFORE the lock */
158 4005           cf_rwlock_wrlock(h);
159 4005           RETVAL = cf_remove_locked(h, s, n);
160 4005           __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
161 4005           cf_rwlock_wrunlock(h);
162             OUTPUT:
163             RETVAL
164              
165             void
166             clear(self)
167             SV *self
168             PREINIT:
169 1 50         EXTRACT(self);
    50          
    50          
170             CODE:
171 1           cf_rwlock_wrlock(h);
172 1           cf_clear_locked(h);
173 1           __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
174 1           cf_rwlock_wrunlock(h);
175              
176             UV
177             count(self)
178             SV *self
179             PREINIT:
180 13 50         EXTRACT(self);
    50          
    50          
181             UV n;
182             CODE:
183 13           cf_rwlock_rdlock(h);
184 13           n = (UV)h->hdr->count;
185 13           cf_rwlock_rdunlock(h);
186 13 50         RETVAL = n;
187             OUTPUT:
188             RETVAL
189              
190             UV
191             capacity(self)
192             SV *self
193             PREINIT:
194 3 50         EXTRACT(self);
    50          
    50          
195             CODE:
196 3 50         RETVAL = (UV)h->hdr->capacity;
197             OUTPUT:
198             RETVAL
199              
200             UV
201             buckets(self)
202             SV *self
203             PREINIT:
204 3 50         EXTRACT(self);
    50          
    50          
205             CODE:
206 3 50         RETVAL = (UV)h->hdr->num_buckets;
207             OUTPUT:
208             RETVAL
209              
210             UV
211             slots(self)
212             SV *self
213             PREINIT:
214 4 50         EXTRACT(self);
    50          
    50          
215             CODE:
216 4 50         RETVAL = (UV)(h->hdr->num_buckets * (uint64_t)CF_SLOTS);
217             OUTPUT:
218             RETVAL
219              
220             SV *
221             stats(self)
222             SV *self
223             PREINIT:
224 3 50         EXTRACT(self);
    50          
    50          
225             CODE:
226             {
227             uint64_t count, capacity, num_buckets, slots_total, ops, mmap_size;
228             /* Snapshot under the lock; do all (croak-capable) Perl allocation after
229             releasing it -- so an OOM in newHV/newSVuv can never strand the lock. */
230 3           cf_rwlock_rdlock(h);
231 3           count = h->hdr->count;
232 3           capacity = h->hdr->capacity;
233 3           num_buckets = h->hdr->num_buckets;
234 3           ops = h->hdr->stat_ops;
235 3           cf_rwlock_rdunlock(h);
236 3           slots_total = num_buckets * (uint64_t)CF_SLOTS;
237 3           mmap_size = (uint64_t)h->mmap_size;
238              
239 3           HV *hv = newHV();
240 3           hv_stores(hv, "capacity", newSVuv((UV)capacity));
241 3           hv_stores(hv, "buckets", newSVuv((UV)num_buckets));
242 3           hv_stores(hv, "slots", newSVuv((UV)slots_total));
243 3           hv_stores(hv, "count", newSVuv((UV)count));
244 3           hv_stores(hv, "fill_ratio", newSVnv((double)count / (double)slots_total)); /* slots_total >= CF_MIN_BUCKETS*CF_SLOTS, never 0 */
245 3           hv_stores(hv, "ops", newSVuv((UV)ops));
246 3           hv_stores(hv, "mmap_size", newSVuv((UV)mmap_size));
247 3           RETVAL = newRV_noinc((SV *)hv);
248             }
249             OUTPUT:
250             RETVAL
251              
252             SV *
253             path(self)
254             SV *self
255             PREINIT:
256 3 50         EXTRACT(self);
    50          
    50          
257             CODE:
258 3 100         RETVAL = h->path ? newSVpv(h->path, 0) : &PL_sv_undef;
259             OUTPUT:
260             RETVAL
261              
262             int
263             memfd(self)
264             SV *self
265             PREINIT:
266 3 50         EXTRACT(self);
    50          
    50          
267             CODE:
268 3 50         RETVAL = h->backing_fd;
269             OUTPUT:
270             RETVAL
271              
272             void
273             sync(self)
274             SV *self
275             PREINIT:
276 2 50         EXTRACT(self);
    50          
    50          
277             CODE:
278 2 50         if (cf_msync(h) != 0) croak("sync: %s", strerror(errno));
279              
280             void
281             unlink(self, ...)
282             SV *self
283             CODE:
284 3 100         if (sv_isobject(self) && sv_derived_from(self, "Data::CuckooFilter::Shared")) {
    50          
285 1           CfHandle *h = INT2PTR(CfHandle*, SvIV(SvRV(self)));
286 1 50         if (h && h->path) unlink(h->path);
    50          
287 1 50         } else if (items >= 2 && SvOK(ST(1))) {
    50          
288 1           unlink(SvPV_nolen(ST(1)));
289             }