File Coverage

Shared.xs
Criterion Covered Total %
statement 97 100 97.0
branch 90 146 61.6
condition n/a
subroutine n/a
pod n/a
total 187 246 76.0


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 "hll.h"
7              
8             #define EXTRACT(sv) \
9             if (!sv_isobject(sv) || !sv_derived_from(sv, "Data::HyperLogLog::Shared")) \
10             croak("Expected a Data::HyperLogLog::Shared object"); \
11             HllHandle *h = INT2PTR(HllHandle*, SvIV(SvRV(sv))); \
12             if (!h) croak("Attempted to use a destroyed Data::HyperLogLog::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::HyperLogLog::Shared PACKAGE = Data::HyperLogLog::Shared
21              
22             PROTOTYPES: DISABLE
23              
24             SV *
25             new(class, path = &PL_sv_undef, precision = 14)
26             const char *class
27             SV *path
28             UV precision
29             PREINIT:
30             char errbuf[HLL_ERR_BUFLEN];
31             CODE:
32 23 100         const char *p = SvOK(path) ? SvPV_nolen(path) : NULL;
33 23 100         if (precision < HLL_MIN_PRECISION || precision > HLL_MAX_PRECISION)
    100          
34 2           croak("Data::HyperLogLog::Shared->new: precision must be between %d and %d",
35             HLL_MIN_PRECISION, HLL_MAX_PRECISION);
36 21           HllHandle *h = hll_create(p, (uint32_t)precision, errbuf);
37 21 100         if (!h) croak("Data::HyperLogLog::Shared->new: %s", errbuf);
38 20           MAKE_OBJ(class, h);
39             OUTPUT:
40             RETVAL
41              
42             SV *
43             new_memfd(class, name = &PL_sv_undef, precision = 14)
44             const char *class
45             SV *name
46             UV precision
47             PREINIT:
48             char errbuf[HLL_ERR_BUFLEN];
49             CODE:
50 4 100         const char *nm = SvOK(name) ? SvPV_nolen(name) : NULL; /* undef -> default label */
51 4 100         if (precision < HLL_MIN_PRECISION || precision > HLL_MAX_PRECISION)
    100          
52 2           croak("Data::HyperLogLog::Shared->new_memfd: precision must be between %d and %d",
53             HLL_MIN_PRECISION, HLL_MAX_PRECISION);
54 2           HllHandle *h = hll_create_memfd(nm, (uint32_t)precision, errbuf);
55 2 50         if (!h) croak("Data::HyperLogLog::Shared->new_memfd: %s", errbuf);
56 2           MAKE_OBJ(class, h);
57             OUTPUT:
58             RETVAL
59              
60             SV *
61             new_from_fd(class, fd)
62             const char *class
63             int fd
64             PREINIT:
65             char errbuf[HLL_ERR_BUFLEN];
66             CODE:
67 1           HllHandle *h = hll_open_fd(fd, errbuf);
68 1 50         if (!h) croak("Data::HyperLogLog::Shared->new_from_fd: %s", errbuf);
69 1           MAKE_OBJ(class, h);
70             OUTPUT:
71             RETVAL
72              
73             void
74             DESTROY(self)
75             SV *self
76             CODE:
77 24 50         if (sv_isobject(self) && sv_derived_from(self, "Data::HyperLogLog::Shared")) {
    50          
78 24           HllHandle *h = INT2PTR(HllHandle*, SvIV(SvRV(self)));
79 24 100         if (h) { sv_setiv(SvRV(self), 0); hll_destroy(h); } /* null first: activates EXTRACT's use-after-destroy croak + makes a double DESTROY a no-op */
80             }
81              
82             int
83             add(self, item)
84             SV *self
85             SV *item
86             PREINIT:
87 420706 50         EXTRACT(self);
    50          
    50          
88             STRLEN n;
89             const char *s;
90             CODE:
91 420706           s = SvPVbyte(item, n);
92 420706           hll_rwlock_wrlock(h);
93 420706           RETVAL = hll_add_locked(h, s, n);
94 420706           __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
95 420706           hll_rwlock_wrunlock(h);
96             OUTPUT:
97             RETVAL
98              
99             UV
100             add_many(self, items)
101             SV *self
102             SV *items
103             PREINIT:
104 6 50         EXTRACT(self);
    50          
    50          
105             AV *av;
106             IV top;
107 6 50         UV added = 0;
108             CODE:
109 6 50         if (!SvROK(items) || SvTYPE(SvRV(items)) != SVt_PVAV)
    50          
110 0           croak("Data::HyperLogLog::Shared->add_many: expected an array reference");
111 6           av = (AV *)SvRV(items);
112 6           top = av_len(av); /* last index, -1 if empty */
113             {
114 6 100         STRLEN cnt = (top >= 0) ? (STRLEN)(top + 1) : 0, i;
115 6           const char **ps = NULL; STRLEN *ls = NULL;
116 6 100         if (cnt) { /* resolve all bytes BEFORE locking */
117 5 50         Newx(ps, cnt, const char *); SAVEFREEPV(ps); /* freed on return OR unwind */
118 5 50         Newx(ls, cnt, STRLEN); SAVEFREEPV(ls);
119 8006 100         for (i = 0; i < cnt; i++) { /* a croak here holds NO lock; SAVEFREEPV cleans up */
120 8002           SV **el = av_fetch(av, (SSize_t)i, 0);
121 8002 50         if (el && *el) ps[i] = SvPVbyte(*el, ls[i]);
    50          
122 0           else { ps[i] = ""; ls[i] = 0; }
123             }
124             }
125 5           hll_rwlock_wrlock(h); /* locked region: NO croak-capable calls */
126 8005 100         for (i = 0; i < cnt; i++) added += (UV)hll_add_locked(h, ps[i], ls[i]);
127 5           __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED); /* a call always counts, even an empty batch */
128 5           hll_rwlock_wrunlock(h);
129             }
130 5 50         RETVAL = added;
131             OUTPUT:
132             RETVAL
133              
134             UV
135             count(self)
136             SV *self
137             PREINIT:
138 19 50         EXTRACT(self);
    50          
    100          
139             double E;
140             CODE:
141 18           hll_rwlock_rdlock(h);
142 18           E = hll_count_locked(h);
143 18           hll_rwlock_rdunlock(h);
144 18 100         RETVAL = (UV)(E + 0.5);
145             OUTPUT:
146             RETVAL
147              
148             void
149             merge(self, other)
150             SV *self
151             SV *other
152             PREINIT:
153 3 50         EXTRACT(self);
    50          
    50          
154             CODE:
155 3 50         if (!sv_isobject(other) || !sv_derived_from(other, "Data::HyperLogLog::Shared"))
    50          
156 0           croak("Data::HyperLogLog::Shared->merge: expected a Data::HyperLogLog::Shared object");
157 3           HllHandle *o = INT2PTR(HllHandle*, SvIV(SvRV(other)));
158 3 50         if (!o) croak("Attempted to use a destroyed Data::HyperLogLog::Shared object");
159              
160             /* Snapshot the other's registers under its read lock into a temp
161             * buffer, then release before taking self's write lock. Copying to
162             * a temp avoids holding two locks at once (deadlock-free regardless
163             * of acquisition order between two processes merging each other). */
164 3           uint32_t om = o->hdr->m; /* m is immutable after creation -- compare lock-free */
165 3 100         if (om != h->hdr->m)
166 1           croak("Data::HyperLogLog::Shared->merge: precision mismatch (%u vs %u registers)",
167             h->hdr->m, om);
168              
169             uint8_t *tmp;
170 2           Newx(tmp, (size_t)om, uint8_t);
171 2           SAVEFREEPV(tmp); /* freed on normal return OR croak unwind */
172 2           hll_rwlock_rdlock(o);
173 2           memcpy(tmp, hll_regs(o), (size_t)om);
174 2           hll_rwlock_rdunlock(o);
175              
176 2           hll_rwlock_wrlock(h);
177 2           hll_merge_regs(h, tmp);
178 2           __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
179 2           hll_rwlock_wrunlock(h);
180              
181             void
182             clear(self)
183             SV *self
184             PREINIT:
185 1 50         EXTRACT(self);
    50          
    50          
186             CODE:
187 1           hll_rwlock_wrlock(h);
188 1           hll_clear_locked(h);
189 1           __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
190 1           hll_rwlock_wrunlock(h);
191              
192             UV
193             precision(self)
194             SV *self
195             PREINIT:
196 4 50         EXTRACT(self);
    50          
    50          
197             CODE:
198 4 50         RETVAL = h->hdr->precision;
199             OUTPUT:
200             RETVAL
201              
202             UV
203             registers(self)
204             SV *self
205             PREINIT:
206 2 50         EXTRACT(self);
    50          
    50          
207             CODE:
208 2 50         RETVAL = h->hdr->m;
209             OUTPUT:
210             RETVAL
211              
212             SV *
213             stats(self)
214             SV *self
215             PREINIT:
216 3 50         EXTRACT(self);
    50          
    50          
217             CODE:
218             {
219             double E;
220             uint64_t ops;
221             uint32_t precision, m;
222             /* Snapshot under the lock; do all (croak-capable) Perl allocation after
223             releasing it -- so an OOM in newHV/newSVuv can never strand the lock. */
224 3           hll_rwlock_rdlock(h);
225 3           E = hll_count_locked(h);
226 3           ops = h->hdr->stat_ops;
227 3           precision = h->hdr->precision;
228 3           m = h->hdr->m;
229 3           hll_rwlock_rdunlock(h);
230              
231 3           HV *hv = newHV();
232 3           hv_stores(hv, "precision", newSVuv(precision));
233 3           hv_stores(hv, "registers", newSVuv(m));
234 3           hv_stores(hv, "count", newSVuv((UV)(E + 0.5)));
235 3           hv_stores(hv, "ops", newSVuv(ops));
236 3           hv_stores(hv, "mmap_size", newSVuv((UV)h->mmap_size));
237 3           RETVAL = newRV_noinc((SV *)hv);
238             }
239             OUTPUT:
240             RETVAL
241              
242             SV *
243             path(self)
244             SV *self
245             PREINIT:
246 3 50         EXTRACT(self);
    50          
    50          
247             CODE:
248 3 100         RETVAL = h->path ? newSVpv(h->path, 0) : &PL_sv_undef;
249             OUTPUT:
250             RETVAL
251              
252             int
253             memfd(self)
254             SV *self
255             PREINIT:
256 3 50         EXTRACT(self);
    50          
    50          
257             CODE:
258 3 50         RETVAL = h->backing_fd;
259             OUTPUT:
260             RETVAL
261              
262             void
263             sync(self)
264             SV *self
265             PREINIT:
266 2 50         EXTRACT(self);
    50          
    50          
267             CODE:
268 2 50         if (hll_msync(h) != 0) croak("sync: %s", strerror(errno));
269              
270             void
271             unlink(self, ...)
272             SV *self
273             CODE:
274 3 100         if (sv_isobject(self) && sv_derived_from(self, "Data::HyperLogLog::Shared")) {
    50          
275 1           HllHandle *h = INT2PTR(HllHandle*, SvIV(SvRV(self)));
276 1 50         if (h && h->path) unlink(h->path);
    50          
277 1 50         } else if (items >= 2 && SvOK(ST(1))) {
    50          
278 1           unlink(SvPV_nolen(ST(1)));
279             }