File Coverage

c/common.c
Criterion Covered Total %
statement 88 106 83.0
branch 60 80 75.0
condition n/a
subroutine n/a
pod n/a
total 148 186 79.5


line stmt bran cond sub pod time code
1             /* Because I don't know how to use sv_setref_pv() correctly. */
2              
3 82           static SV *fu_selfobj_(pTHX_ SV **self, void *obj, const char *klass) {
4 82           *self = newSViv(PTR2IV(obj));
5 82           return sv_bless(sv_2mortal(newRV_noinc(*self)), gv_stashpv(klass, GV_ADD));
6             }
7             /* Write a blessed SV to obj->self and returns a mortal ref to it */
8             #define fu_selfobj(obj, klass) fu_selfobj_(aTHX_ &((obj)->self), obj, klass)
9              
10              
11              
12             /* Return an SV to use for croak_sv() with a HV object.
13             * Adds a "full_message" field including stack trace. */
14              
15             __attribute__((format (printf, 3, 4)))
16 0           static SV *fu_croak_hv(HV *hv, const char *klass, const char *message, ...) {
17             va_list args;
18             SV *sv;
19             dTHX;
20 0           dSP;
21              
22 0           va_start(args, message);
23 0           sv = vnewSVpvf(message, &args);
24 0           va_end(args);
25              
26 0           ENTER;
27 0           SAVETMPS;
28 0 0         PUSHMARK(SP);
29 0 0         mXPUSHs(sv);
30 0           PUTBACK;
31 0           call_pv("Carp::longmess", G_SCALAR);
32 0           hv_stores(hv, "full_message", SvREFCNT_inc(POPs));
33 0 0         FREETMPS;
34 0           LEAVE;
35              
36 0           return sv_bless(sv_2mortal(newRV_noinc((SV *)hv)), gv_stashpv(klass, GV_ADD));
37             }
38              
39             __attribute__((noreturn, format (printf, 1, 2)))
40 17           static void fu_confess(const char *message, ...) {
41             va_list args;
42             SV *sv;
43             dTHX;
44 17           dSP;
45              
46 17           va_start(args, message);
47 17           sv = vnewSVpvf(message, &args);
48 17           va_end(args);
49              
50 17           ENTER;
51 17           SAVETMPS;
52 17 50         PUSHMARK(SP);
53 17 50         mXPUSHs(sv);
54 17           PUTBACK;
55 17           call_pv("Carp::confess", G_DISCARD);
56             /* Won't happen, but a safe fallback */
57 0           croak("%s", SvPV_nolen(sv));
58             }
59              
60              
61              
62             /* Custom string builder, should be slightly faster than using Sv* macros directly. */
63              
64             typedef struct {
65             SV *sv;
66             SV *mortal;
67             char *cur;
68             char *end;
69             size_t maxlen;
70             int setutf8;
71             char sbuf[4096];
72             } fustr;
73              
74 5936           static void fustr_init_(pTHX_ fustr *s, SV *mortal, size_t maxlen) {
75 5936           s->sv = NULL;
76 5936           s->cur = s->sbuf;
77 5936           s->end = s->sbuf + (maxlen > sizeof s->sbuf ? sizeof s->sbuf : maxlen);
78 5936           s->maxlen = maxlen;
79 5936           s->mortal = mortal;
80 5936           s->setutf8 = 0;
81 5936           }
82              
83             #define fustr_start(s) (((s)->sv ? SvPVX((s)->sv) : (s)->sbuf))
84             #define fustr_len(s) ((s)->cur - fustr_start(s))
85              
86 1046           static void fustr_grow(pTHX_ fustr *s, size_t add) {
87 1046 100         size_t off = fustr_len(s);
88 1046           size_t newlen = sizeof s->sbuf;
89             char *buf;
90 1046           add += off;
91 1046 100         if (add > s->maxlen) croak("maximum string length exceeded");
92             /* Increment to next power of two; SvGROW's default strategy is slow */
93 2554 100         while (newlen < add) newlen <<= 1;
94 1045 50         if (newlen > s->maxlen) newlen = s->maxlen;
95 1045 100         if (s->sv) {
96 224 50         buf = SvGROW(s->sv, newlen);
    50          
97             } else {
98 821 100         if (s->mortal) {
99 410           s->sv = s->mortal;
100 410           sv_setpv_bufsize(s->sv, off, newlen);
101             } else {
102 411           s->sv = newSV(newlen);
103             }
104 821           SvPOK_only(s->sv);
105 821           buf = SvPVX(s->sv);
106 821           memcpy(buf, s->sbuf, off);
107             }
108 1045           s->cur = buf + off;
109 1045           s->end = buf + (SvLEN(s->sv) > s->maxlen ? s->maxlen : SvLEN(s->sv));
110 1045           }
111              
112 4760908           static inline void fustr_reserve_(pTHX_ fustr *s, size_t add) {
113 4760908 100         if (UNLIKELY(s->end < s->cur + add)) fustr_grow(aTHX_ s, add);
114 4760907           }
115              
116 4951           static inline void fustr_write_(pTHX_ fustr *s, const char *str, size_t n) {
117 4951           fustr_reserve_(aTHX_ s, n);
118 4951           memcpy(s->cur, str, n);
119 4951           s->cur += n;
120 4951           }
121              
122 5196           static inline void fustr_write_ch_(pTHX_ fustr *s, char x) {
123 5196           fustr_reserve_(aTHX_ s, 1);
124 5196           *(s->cur++) = x;
125 5196           }
126              
127             /* Adds n uninitialized bytes to the string and returns a buffer to write the data to */
128 31           static inline char *fustr_write_buf_(pTHX_ fustr *s, size_t n) {
129 31           fustr_reserve_(aTHX_ s, n);
130 31           char *buf = s->cur;
131 31           s->cur += n;
132 31           return buf;
133             }
134              
135 5370           static SV *fustr_done_(pTHX_ fustr *s) {
136 5370           fustr_reserve_(aTHX_ s, 1);
137 5370           *s->cur = 0;
138 5370 100         if (s->sv) {
139 821           SvCUR_set(s->sv, s->cur - SvPVX(s->sv));
140             // TODO: SvPV_shrink_to_cur?
141             } else {
142 4549 100         s->sv = newSVpvn_flags(s->sbuf, s->cur - s->sbuf, s->mortal ? SVs_TEMP : 0);
143             }
144 5370 100         if (s->setutf8) SvUTF8_on(s->sv);
145 5370           return s->sv;
146             }
147              
148             #define fustr_init(a,b,c) fustr_init_(aTHX_ a,b,c)
149             #define fustr_reserve(a,b) fustr_reserve_(aTHX_ a,b)
150             #define fustr_write(a,b,c) fustr_write_(aTHX_ a,b,c)
151             #define fustr_write_ch(a,b) fustr_write_ch_(aTHX_ a,b)
152             #define fustr_write_buf(a,b) fustr_write_buf_(aTHX_ a,b)
153             #define fustr_done(a) fustr_done_(aTHX_ a)
154              
155              
156             #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
157             #define fu_bswap(bits, out, in) ({ U##bits tmpswap; memcpy(&tmpswap, in, bits>>3); tmpswap = __builtin_bswap##bits(tmpswap); memcpy(out, &tmpswap, bits>>3); })
158             #else
159             #define fu_bswap(bits, out, in) memcpy(out, in, bits>>3)
160             #endif
161              
162             #define fu_frombeT(T, bits, buf) ({ T tmpval; fu_bswap(bits, &tmpval, buf); tmpval; })
163             #define fu_frombeI(bits, buf) fu_frombeT(I##bits, bits, buf)
164             #define fu_frombeU(bits, buf) fu_frombeT(U##bits, bits, buf)
165              
166             #define fu_tobeT(T, bits, out, in) ({ T tmpval = in; fu_bswap(bits, out, &tmpval); })
167             #define fu_tobeI(bits, out, in) fu_tobeT(I##bits, bits, out, in)
168             #define fu_tobeU(bits, out, in) fu_tobeT(U##bits, bits, out, in)
169              
170             #define fustr_writebeT(T, bits, s, in) fu_tobeT(T, bits, fustr_write_buf(s, bits>>3), in)
171             #define fustr_writebeI(bits, s, in) fustr_writebeT(I##bits, bits, s, in)
172             #define fustr_writebeU(bits, s, in) fustr_writebeT(U##bits, bits, s, in)
173              
174              
175             /* Return the difference between two struct timespecs as fractional seconds. */
176 0           static double fu_timediff(const struct timespec *a, const struct timespec *b) {
177 0           return ((double)(a->tv_sec - b->tv_sec)) + (double)(a->tv_nsec - b->tv_nsec) / 1000000000.0;
178             }
179              
180              
181 72           static int fu_hexdig(char x) {
182 72 50         return x >= '0' && x <= '9' ? x-'0' : x >= 'A' && x <= 'F' ? x-'A'+10 : x >= 'a' && x <= 'f' ? x-'a'+10 : 0x10000;
    100          
    50          
    100          
    50          
    100          
183             }
184              
185              
186              
187             /* -1 if arg is not a bool, 0 on false, 1 on true */
188 5876           static int fu_2bool(pTHX_ SV *val) {
189 5876 100         if (SvIsBOOL(val)) return BOOL_INTERNALS_sv_isbool_true(val) ? 1 : 0;
    50          
    100          
190 5855 100         if (!SvROK(val)) return -1;
191 1074           SV *rv = SvRV(val);
192              
193 1074 100         if (SvOBJECT(rv)) {
194 14           HV *stash = SvSTASH(rv);
195             /* Historical: "JSON::XS::Boolean", not used by JSON::XS since 3.0 in 2013 */
196 14 100         if (stash == gv_stashpvs("JSON::PP::Boolean", 0) /* Also covers Types::Serialiser::Boolean and used by a bunch of other modules */
197 12 100         || stash == gv_stashpvs("boolean", 0)
198 10 50         || stash == gv_stashpvs("Mojo::JSON::_Bool", 0)
199 10 50         || stash == gv_stashpvs("JSON::Tiny::_Bool", 0))
200 4           return !!SvIV(rv);
201 10           return -1;
202             }
203              
204             /* \0 or \1 */
205 1060 100         if (SvTYPE(rv) < SVt_PVAV) {
206 8 100         if (SvIOK(rv)) {
207 6           IV iv = SvIV(rv);
208 6 100         return iv == 0 ? 0 : iv == 1 ? 1 : -1;
    100          
209 2 50         } else if (SvOK(rv)) {
210             STRLEN len;
211 2           char *str = SvPV_nomg(rv, len);
212 2 50         return len != 1 ? -1 : *str == '0' ? 0 : *str == '1' ? 1 : -1;
    100          
    50          
213             }
214             }
215 1052           return -1;
216             }