File Coverage

jsonxs_inline.h
Criterion Covered Total %
statement 67 101 66.3
branch 56 84 66.6
condition n/a
subroutine n/a
pod n/a
total 123 185 66.4


line stmt bran cond sub pod time code
1             /**
2             * This header file contains static functions adapted from
3             * Marc Lehmanns' JSON::XS, particularly because it's fast,
4             * doesn't seem to rely on any external structures, and because
5             * numeric conversions aren't my ideas of fun.
6             */
7              
8             #ifndef JSONXS_INLINE_H_
9             #define JSONXS_INLINE_H_
10              
11             #include "perl-jsonsl.h"
12             #if __GNUC__ >= 3
13             # define expect(expr,value) __builtin_expect ((expr), (value))
14             # define INLINE static inline
15             #else
16             # define expect(expr,value) (expr)
17             # define INLINE static
18             #endif
19              
20             #define ERR die
21             #define expect_false(expr) expect ((expr) != 0, 0)
22             #define expect_true(expr) expect ((expr) != 0, 1)
23              
24             #define jsonxs__atof_scan1(s,a,e,p,m) jsonxs__atof_scan1_THX(aTHX_ s,a,e,p,m)
25             INLINE void
26 20           jsonxs__atof_scan1_THX(pTHX_ const char *s,
27             NV *accum, int *expo, int postdp,
28             int maxdepth)
29             {
30 20           UV uaccum = 0;
31 20           int eaccum = 0;
32              
33             /* if we recurse too deep, skip all remaining digits
34             to avoid a stack overflow attack */
35 20 50         if (expect_false (--maxdepth <= 0))
36 0 0         while (((U8)*s - '0') < 10)
37 0           ++s;
38              
39             for (;;)
40             {
41 61           U8 dig = (U8)*s - '0';
42              
43 61 100         if (expect_false (dig >= 10))
44             {
45 20 100         if (dig == (U8)((U8)'.' - (U8)'0'))
46             {
47 8           ++s;
48 8           jsonxs__atof_scan1(s, accum, expo, 1, maxdepth);
49             }
50 12 100         else if ((dig | ' ') == 'e' - '0')
51             {
52 8           int exp2 = 0;
53 8           int neg = 0;
54              
55 8           ++s;
56              
57 8 100         if (*s == '-')
58             {
59 2           ++s;
60 2           neg = 1;
61             }
62 6 100         else if (*s == '+')
63 2           ++s;
64              
65 17 100         while ((dig = (U8)*s - '0') < 10)
66 9           exp2 = exp2 * 10 + *s++ - '0';
67              
68 8 100         *expo += neg ? -exp2 : exp2;
69             }
70              
71 20           break;
72             }
73              
74 41           ++s;
75              
76 41           uaccum = uaccum * 10 + dig;
77 41           ++eaccum;
78              
79             /*
80             if we have too many digits, then recurse for more
81             we actually do this for rather few digits
82             */
83 41 50         if (uaccum >= (UV_MAX - 9) / 10)
84             {
85 0 0         if (postdp) *expo -= eaccum;
86 0           jsonxs__atof_scan1 (s, accum, expo, postdp, maxdepth);
87 0 0         if (postdp) *expo += eaccum;
88              
89 0           break;
90             }
91 41           }
92              
93             /*
94             // this relies greatly on the quality of the pow ()
95             // implementation of the platform, but a good
96             // implementation is hard to beat.
97             // (IEEE 754 conformant ones are required to be exact)
98             *
99             */
100 20 100         if (postdp) *expo -= eaccum;
101 20           *accum += uaccum * Perl_pow (10., *expo);
102 20           *expo += eaccum;
103 20           }
104              
105             #define jsonxs__atof(s) jsonxs__atof_THX(aTHX_ s)
106             INLINE NV
107 12           jsonxs__atof_THX (pTHX_ const char *s)
108             {
109 12           NV accum = 0.;
110 12           int expo = 0;
111 12           int neg = 0;
112              
113 12 100         if (*s == '-')
114             {
115 2           ++s;
116 2           neg = 1;
117             }
118              
119             /* a recursion depth of ten gives us >>500 bits */
120 12           jsonxs__atof_scan1(s, &accum, &expo, 0, 10);
121              
122 12 100         return neg ? -accum : accum;
123             }
124              
125             #define jsonxs_inline_process_number(s) jsonxs_inline_process_number_THX(aTHX_ s)
126              
127             INLINE SV *
128 12           jsonxs_inline_process_number_THX(pTHX_ const char *start)
129             {
130              
131 12           int is_nv = 0;
132 12           const char *c = start;
133              
134 12 100         if (*c == '-')
135 2           ++c;
136              
137 12 100         if (*c == '0') {
138 3           ++c;
139 3 50         if (*c >= '0' && *c <= '9') {
    0          
140 0           ERR("malformed number (leading zero must not be followed by another digit)");
141             }
142 9 50         } else if (*c < '0' || *c > '9') {
    50          
143 0           ERR("malformed number (no digits after initial minus)");
144             } else {
145             do {
146 10           ++c;
147 10 100         } while (*c >= '0' && *c <= '9');
    100          
148             }
149              
150 12 100         if (*c == '.') {
151 8           ++c;
152              
153 8 50         if (*c < '0' || *c > '9')
    50          
154 0           ERR("malformed number (no digits after decimal point)");
155              
156             do {
157 28           ++c;
158 28 100         } while (*c >= '0' && *c <= '9');
    100          
159              
160 8           is_nv = 1;
161             }
162              
163 12 100         if (*c == 'e' || *c == 'E') {
    100          
164 8           ++c;
165              
166 8 100         if (*c == '-' || *c == '+')
    100          
167 4           ++c;
168              
169 8 50         if (*c < '0' || *c > '9')
    50          
170 0           ERR("malformed number (no digits after exp sign)");
171              
172             do {
173 9           ++c;
174 9 100         } while (*c >= '0' && *c <= '9');
    100          
175              
176 8           is_nv = 1;
177             }
178              
179 12 50         if (!is_nv) {
180 0           int len = c - start;
181              
182             /* special case the rather common 1..5-digit-int case */
183 0 0         if (*start == '-')
184 0           switch (len) {
185             case 2:
186 0           return newSViv (-(IV)( start [1] - '0' * 1));
187             case 3:
188 0           return newSViv (-(IV)( start [1] * 10 + start [2] - '0' * 11));
189             case 4:
190 0           return newSViv (-(IV)( start [1] * 100 + start [2] * 10 + start [3] - '0' * 111));
191             case 5:
192 0           return newSViv (-(IV)( start [1] * 1000 + start [2] * 100 + start [3] * 10 + start [4] - '0' * 1111));
193             case 6:
194 0           return newSViv (-(IV)(start [1] * 10000 + start [2] * 1000 + start [3] * 100 + start [4] * 10 + start [5] - '0' * 11111));
195             }
196             else
197 0           switch (len) {
198             case 1:
199 0           return newSViv ( start [0] - '0' * 1);
200             case 2:
201 0           return newSViv ( start [0] * 10 + start [1] - '0' * 11);
202             case 3:
203 0           return newSViv ( start [0] * 100 + start [1] * 10 + start [2] - '0' * 111);
204             case 4:
205 0           return newSViv ( start [0] * 1000 + start [1] * 100 + start [2] * 10 + start [3] - '0' * 1111);
206             case 5:
207 0           return newSViv ( start [0] * 10000 + start [1] * 1000 + start [2] * 100 + start [3] * 10 + start [4] - '0' * 11111);
208             }
209              
210             {
211             UV uv;
212 0           int numtype = grok_number (start, len, &uv);
213 0 0         if (numtype & IS_NUMBER_IN_UV
214             ) {
215 0 0         if (numtype & IS_NUMBER_NEG)
216             {
217 0 0         if (uv < (UV) IV_MIN
218             )
219 0           return newSViv (-(IV)uv);
220             } else
221 0           return newSVuv (uv);
222             }
223             }
224              
225 0           len -= *start == '-' ? 1 : 0;
226              
227             /* does not fit into IV or UV, try NV */
228 0 0         if (len <= NV_DIG
229             )
230             /* fits into NV without loss of precision */
231 0           return newSVnv (jsonxs__atof (start));
232              
233             /* everything else fails, convert it to a string */
234 0           return newSVpvn (start, c - start);
235             }
236              
237             /* loss of precision here */
238 12           return newSVnv (jsonxs__atof (start));
239             }
240              
241             #undef ERR
242             #undef expect_false
243             #undef expect_true
244              
245             #endif /* JSONXS_INLINE_H_ */