File Coverage

Uniqnum.xs
Criterion Covered Total %
statement 45 56 80.3
branch 68 96 70.8
condition n/a
subroutine n/a
pod n/a
total 113 152 74.3


line stmt bran cond sub pod time code
1            
2             #define PERL_NO_GET_CONTEXT 1
3            
4            
5             #include "EXTERN.h"
6             #include "perl.h"
7             #include "XSUB.h"
8            
9            
10             #ifndef sv_setpvs
11             # define sv_setpvs(sv, str) sv_setpvn(sv, str "", sizeof(str) - 1)
12             #endif
13            
14             /* For uniqnum, define ACTUAL_NVSIZE to be the number *
15             * of bytes that are actually used to store the NV */
16            
17             #if defined(USE_LONG_DOUBLE) && LDBL_MANT_DIG == 64
18             #define ACTUAL_NVSIZE 10
19             #else
20             #define ACTUAL_NVSIZE NVSIZE
21             #endif
22            
23             /* Detect "DoubleDouble" nvtype */
24            
25             #if defined(USE_LONG_DOUBLE) && LDBL_MANT_DIG == 106
26             # define NV_IS_DOUBLEDOUBLE
27             #endif
28            
29             #ifndef SvUOK
30             # define SvUOK SvIsUV
31             #endif
32            
33 0           int uv_fits_double(UV arg) {
34            
35             /* This function is no longer used. *
36             * The value passed was always > 9007199254740992 *
37             * and always <= 18446744073709551615. *
38             * Return true if there are no more than 51 bits *
39             * between the most significant set bit and the *
40             * least significant set bit - in which case the *
41             * value can be exactly represented by a double. */
42            
43 0 0         while(!(arg & 1)) {
44 0           arg >>= 1;
45 0 0         if(arg < 9007199254740992) return 1;
46             }
47            
48 0           return 0;
49             }
50            
51 26           void uniqnum(pTHX_ SV * input_sv, ...) {
52 26           dXSARGS;
53 26           int retcount = 0;
54             int index;
55 26           SV **args = &PL_stack_base[ax];
56             HV *seen;
57            
58             SV *keysv;
59             SV *arg;
60             NV nv_arg;
61            
62             #ifdef HV_FETCH_EMPTY_HE
63             HE* he;
64             #endif
65            
66 26 50         if(items == 0 || (items == 1 && !SvGAMAGIC(args[0]) && SvOK(args[0]))) {
    100          
    50          
    50          
    0          
    0          
    50          
    50          
    50          
67             /* Optimise for the case of the empty list or a defined nonmagic
68             * singleton. Leave a singleton magical||undef for the regular case */
69 0           retcount = items;
70 0           goto finish;
71             }
72            
73 26           sv_2mortal((SV *)(seen = newHV()));
74            
75            
76             /* uniqnum */
77             /* A temporary buffer for number stringification */
78 26           keysv = sv_newmortal();
79            
80 271 100         for(index = 0 ; index < items ; index++) {
81 245           arg = args[index];
82            
83 245 100         if(SvGAMAGIC(arg))
    100          
    50          
    50          
84             /* clone the value so we don't invoke magic again */
85 6           arg = sv_mortalcopy(arg);
86            
87 245 100         if(SvOK(arg) && !(SvUOK(arg) || SvIOK(arg) || SvNOK(arg))) {
    50          
    50          
    100          
    100          
    100          
88             #if PERL_VERSION >= 8
89 141 50         SvIV(arg); /* sets SVf_IOK/SVf_IsUV if it's an integer */
90             #else
91             SvNV(arg); /* SvIV() sets SVf_IOK even on floats on 5.6 */
92             #endif
93             }
94             #if NVSIZE > IVSIZE /* $Config{nvsize} > $Config{ivsize} */
95             /* Avoid altering arg's flags */
96             if(SvUOK(arg)) nv_arg = (NV)SvUV(arg);
97             else if(SvIOK(arg)) nv_arg = (NV)SvIV(arg);
98             else nv_arg = SvNV(arg);
99            
100             /* use 0 for all zeros */
101             if(nv_arg == 0) sv_setpvs(keysv, "0");
102            
103             /* for NaN, use the platform's normal stringification */
104             else if (nv_arg != nv_arg) sv_setpvf(keysv, "%" NVgf, nv_arg);
105             #ifdef NV_IS_DOUBLEDOUBLE
106             /* If the least significant double is zero, it could be either 0.0 *
107             * or -0.0. We therefore ignore the least significant double and *
108             * assign to keysv the bytes of the most significant double only. */
109             else if(nv_arg == (double)nv_arg) {
110             double double_arg = (double)nv_arg;
111             sv_setpvn(keysv, (char *) &double_arg, 8);
112             }
113             #endif
114             else {
115             /* Use the byte structure of the NV. *
116             * ACTUAL_NVSIZE == sizeof(NV) minus the number of bytes *
117             * that are allocated but never used. (It is only the 10-byte *
118             * extended precision long double that allocates bytes that are *
119             * never used. For all other NV types ACTUAL_NVSIZE == sizeof(NV). */
120             sv_setpvn(keysv, (char *) &nv_arg, ACTUAL_NVSIZE);
121             }
122             #else /* $Config{nvsize} == $Config{ivsize} == 8 */
123 322 100         if( SvIOK(arg) || !SvOK(arg) ) {
    100          
    50          
    50          
124             /* It doesn't matter if uok is TRUE */
125 77 100         IV iv = SvIV(arg);
126            
127             /* use "0" for all zeros */
128 77 100         if(iv == 0) sv_setpvs(keysv, "0");
129            
130             else {
131 71           int uok = SvUOK(arg);
132 71 100         int sign = ( iv > 0 || uok ) ? 1 : -1;
    100          
133            
134             /* Set keysv to the bytes of SvNV(arg) if and only if the integer value *
135             * held by arg can be represented exactly as a double - ie if there are *
136             * no more than 51 bits between its least significant set bit and its *
137             * most significant set bit. *
138             * The neatest approach I could find was provided by roboticus at: *
139             * https://www.perlmonks.org/?node_id=11113490 *
140             * First, identify the lowest set bit and assign its value to an IV. *
141             * Note that this value will always be > 0, and always a power of 2. */
142 71           IV lowest_set = iv & -iv;
143            
144             /* Second, shift it left 53 bits to get location of arg's highest *
145             * "allowed" set bit. *
146             * NOTE: If lowest set bit is initially far enough left, then this left *
147             * shift operation will result in a value of 0, which is fine. *
148             * Then subtract 1 so that all of the ("allowed") bits below the set bit *
149             * are 1 && all other ("disallowed") bits are set to 0. *
150             * (If the value prior to subtraction was 0, then subtracing 1 will set *
151             * all bits - which is also fine.) */
152 71           UV valid_bits = (lowest_set << 53) - 1;
153            
154             /* The value of arg can be exactly represented by a double unless one *
155             * or more of its "disallowed" bits are set - ie if iv & (~valid_bits) *
156             * is untrue. However, if (iv < 0 && !SvUOK(arg)) we need to multiply iv *
157             * by -1 prior to performing that '&' operation - so multiply iv by sign.*/
158 71 100         if( !((iv * sign) & (~valid_bits)) ) {
159             /* Avoid altering arg's flags */
160 62 100         nv_arg = uok ? (NV)SvUV(arg) : (NV)SvIV(arg);
    50          
    50          
161 62           sv_setpvn(keysv, (char *) &nv_arg, 8);
162             }
163             else {
164             /* Read in the bytes, rather than the numeric value of the IV/UV as *
165             * this is more efficient, despite having to sv_catpvn an extra byte.*/
166 9           sv_setpvn(keysv, (char *) &iv, 8);
167             /* We add an extra byte to distinguish between an IV/UV and an NV. *
168             * We also use that byte to distinguish between a -ve IV and a UV. */
169 9 100         if(uok) sv_catpvn(keysv, "U", 1);
170 3           else sv_catpvn(keysv, "I", 1);
171             }
172             }
173             }
174             else {
175 168 100         nv_arg = SvNV(arg);
176            
177             /* for NaN, use the platform's normal stringification */
178 168 100         if (nv_arg != nv_arg) sv_setpvf(keysv, "%" NVgf, nv_arg);
179            
180             /* use "0" for all zeros */
181 164 100         else if(nv_arg == 0) sv_setpvs(keysv, "0");
182 59           else sv_setpvn(keysv, (char *) &nv_arg, 8);
183             }
184             #endif
185             #ifdef HV_FETCH_EMPTY_HE
186 245           he = (HE*) hv_common(seen, NULL, SvPVX(keysv), SvCUR(keysv), 0, HV_FETCH_LVALUE | HV_FETCH_EMPTY_HE, NULL, 0);
187 245 100         if (HeVAL(he))
188 134           continue;
189 111           HeVAL(he) = &PL_sv_undef;
190             #else
191             if(hv_exists(seen, SvPVX(keysv), SvCUR(keysv)))
192             continue;
193             hv_store(seen, SvPVX(keysv), SvCUR(keysv), &PL_sv_yes, 0);
194             #endif
195            
196 111 50         if(GIMME_V == G_ARRAY)
    100          
197 90 100         ST(retcount) = SvOK(arg) ? arg : sv_2mortal(newSViv(0));
    50          
    50          
198 111           retcount++;
199             }
200            
201             finish:
202 26 50         if(GIMME_V == G_ARRAY) {
    100          
203 21           XSRETURN(retcount);
204             }
205             else {
206 5           ST(0) = sv_2mortal(newSViv(retcount));
207 26           XSRETURN(1);
208             }
209             }
210            
211 0           int _have_msc_ver(void) {
212             #ifdef _MSC_VER
213             return _MSC_VER;
214             #else
215 0           return 0;
216             #endif
217             }
218            
219            
220             MODULE = List::Uniqnum PACKAGE = List::Uniqnum
221            
222             PROTOTYPES: DISABLE
223            
224            
225             int
226             uv_fits_double (arg)
227             UV arg
228            
229             void
230             uniqnum (input_sv, ...)
231             SV * input_sv
232             PREINIT:
233             I32* temp;
234             PPCODE:
235 26           temp = PL_markstack_ptr++;
236 26           uniqnum(aTHX_ input_sv);
237 26 50         if (PL_markstack_ptr != temp) {
238             /* truly void, because dXSARGS not invoked */
239 0           PL_markstack_ptr = temp;
240 0           XSRETURN_EMPTY; /* return empty stack */
241             }
242             /* must have used dXSARGS; list context implied */
243 26           return; /* assume stack size is correct */
244            
245             int
246             _have_msc_ver ()
247            
248