File Coverage

XS.xs
Criterion Covered Total %
statement 94 102 92.1
branch 81 140 57.8
condition n/a
subroutine n/a
pod n/a
total 175 242 72.3


line stmt bran cond sub pod time code
1             //#define PERL_NO_GET_CONTEXT
2              
3             // PERL_NO_GET_CONTEXT is not used here, so it's OK to define it after inculding these files
4             #include "EXTERN.h"
5             #include "perl.h"
6              
7             // There are a lot of macro about threads: USE_ITHREADS, USE_5005THREADS, I_PTHREAD, I_MACH_CTHREADS, OLD_PTHREADS_API
8             // This symbol, if defined, indicates that Perl should be built to use the interpreter-based threading implementation.
9             #ifndef USE_ITHREADS
10             # define PERL_NO_GET_CONTEXT
11             #endif
12              
13             //#ifdef USE_ITHREADS
14             //# warning USE_ITHREADS: THREADS ARE ON
15             //#endif
16             //#ifdef USE_5005THREADS
17             //# warning USE_5005THREADS: THREADS ARE ON
18             //#endif
19             //#ifdef I_PTHREAD
20             //# warning I_PTHREAD: THREADS ARE ON
21             //#endif
22             //#ifdef I_MACH_CTHREADS
23             //# warning I_MACH_CTHREADS: THREADS ARE ON
24             //#endif
25             //#ifdef OLD_PTHREADS_API
26             //# warning OLD_PTHREADS_API: THREADS ARE ON
27             //#endif
28              
29             #include "XSUB.h"
30              
31             #include
32              
33             #ifdef I_PTHREAD
34             # include "pthread.h"
35             #endif
36              
37             #ifdef I_MACH_CTHREADS
38             # include "mach/cthreads.h"
39             #endif
40              
41             #include
42              
43             /*
44             inline static void call_srand_if_required (void) {
45             //#if (PERL_VERSION >= 9)
46             if(!PL_srand_called) {
47             (void)seedDrand01((Rand_seed_t)Perl_seed(aTHX));
48             PL_srand_called = TRUE;
49             }
50             }
51             */
52              
53 0           inline static void croak_sv_is_not_an_arrayref (short int pos) {
54             static const char* pattern = "The argument at position %i isn't an array reference";
55 0           croak(pattern, pos);
56             }
57              
58 6           inline static void shuffle_tied_av_last_num_elements (AV *av, SSize_t len, SSize_t num) {
59              
60             static SSize_t rand_index, cur_index;
61             SV *a, *b;
62             SV **ap, **bp;
63              
64 6           cur_index = std::move(len);
65              
66 32 100         while (cur_index >= 1) {
67 26           rand_index = rand() % cur_index; // (cur_index + 1) * Drand01();
68              
69 26 50         ap = av_fetch(av, cur_index, 0);
70 26 50         bp = av_fetch(av, rand_index, 0);
71 26 50         a = (ap ? sv_2mortal( newSVsv(*ap) ) : &PL_sv_undef);
    50          
    50          
72 26 50         b = (bp ? sv_2mortal( newSVsv(*bp) ) : &PL_sv_undef);
    50          
    50          
73 26 50         SvREFCNT_inc_simple_void(a);
74 26 50         SvREFCNT_inc_simple_void(b);
75              
76             // if "av_store" returns NULL, the caller will have to decrement the reference count to avoid a memory leak
77 26 50         if (av_store(av, cur_index, b) == NULL)
    50          
78 26 50         SvREFCNT_dec(b);
79 26 50         mg_set(b);
80              
81 26 50         if (av_store(av, rand_index, a) == NULL)
    50          
82 26 50         SvREFCNT_dec(a);
83 26 50         mg_set(a);
84              
85 26           cur_index--;
86             }
87 6           }
88              
89 1           inline static void shuffle_tied_av_first_num_elements (AV *av, SSize_t len, SSize_t num) {
90              
91             static SSize_t rand_index, cur_index;
92             SV *a, *b;
93             SV **ap, **bp;
94              
95 1           cur_index = 0;
96              
97 5 100         while (cur_index <= num) {
98 4 50         rand_index = cur_index + (len - cur_index) * Drand01(); // cur_index + rand() % (len - cur_index)
99              
100             // perlguts: Note the value so returned does not need to be deallocated, as it is already mortal.
101             // SO, let's bump REFCNT then
102 4 50         ap = av_fetch(av, cur_index, 0);
103 4 50         bp = av_fetch(av, rand_index, 0);
104 4 50         a = (ap ? sv_2mortal( newSVsv(*ap) ) : &PL_sv_undef);
    50          
    50          
105 4 50         b = (bp ? sv_2mortal( newSVsv(*bp) ) : &PL_sv_undef);
    50          
    50          
106 4 50         SvREFCNT_inc_simple_void(b);
107 4 50         SvREFCNT_inc_simple_void(a);
108             //warn("cur_index = %i\trnd = %i\n", cur_index, rand_index);
109              
110             // [MAYCHANGE] After a call to "av_store" on a tied array, the caller will usually
111             // need to call "mg_set(val)" to actually invoke the perl level "STORE" method on the TIEARRAY object.
112 4 50         if (av_store(av, cur_index, b) == NULL)
    50          
113 4 50         SvREFCNT_dec(b);
114 4 50         mg_set(b);
115              
116 4 50         if (av_store(av, rand_index, a) == NULL)
    50          
117 4 50         SvREFCNT_dec(a);
118 4 50         mg_set(a);
119              
120 4           cur_index++;
121             }
122 1           }
123              
124 1000021           inline static void shuffle_av_last_num_elements (AV *av, SSize_t len, SSize_t num) {
125              
126             //call_srand_if_required();
127              
128 1000021 100         if (SvTIED_mg((SV *)av, PERL_MAGIC_tied)) {
    50          
    100          
129 6           shuffle_tied_av_last_num_elements(av, len, num);
130             } else {
131             static SSize_t rand_index, cur_index;
132 1000015           SV **pav = AvARRAY(av);
133             SV* a;
134              
135 1000015           cur_index = std::move(len);
136              
137 11000132 100         while (cur_index >= 0) {
138 10000117 50         rand_index = (cur_index + 1) * Drand01(); // rand() % (cur_index + 1);
139             //warn("cur_index = %i\trnd = %i\n", (int)cur_index, (int)rand_index);
140 10000117           a = std::move((SV*) pav[rand_index]);
141 10000117           pav[rand_index] = std::move(pav[cur_index]);
142 10000117           pav[cur_index] = std::move(a);
143 10000117           cur_index--;
144             }
145             }
146 1000021           }
147              
148 2           inline static void shuffle_av_first_num_elements (AV *av, SSize_t len, SSize_t num) {
149              
150 2           len++;
151              
152             //call_srand_if_required();
153              
154 2 100         if (SvTIED_mg((SV *)av, PERL_MAGIC_tied)) {
    50          
    100          
155 1           shuffle_tied_av_first_num_elements(av, len, num);
156             } else {
157             static SSize_t rand_index, cur_index;
158             SV* a;
159 1           SV **pav = AvARRAY(av);
160              
161 1           cur_index = 0;
162              
163 4 100         while (cur_index <= num) {
164 3 50         rand_index = cur_index + (len - cur_index) * Drand01(); // cur_index + rand() % (len - cur_index);
165             //warn("cur_index = %i\trnd = %i\n", (int)cur_index, (int)rand_index);
166              
167 3           a = std::move((SV*) pav[rand_index]);
168 3           pav[rand_index] = std::move(pav[cur_index]);
169 3           pav[cur_index] = std::move(a);
170 3           cur_index++;
171             }
172             }
173 2           }
174              
175             MODULE = List::Helpers::XS PACKAGE = List::Helpers::XS
176              
177             PROTOTYPES: DISABLE
178              
179             BOOT:
180             #if (PERL_VERSION >= 14)
181 1           sv_setpv((SV*)GvCV(gv_fetchpvs("List::Helpers::XS::shuffle", 0, SVt_PVCV)), "+");
182             #else
183             sv_setpv((SV*)GvCV(gv_fetchpvs("List::Helpers::XS::shuffle", 0, SVt_PVCV)), "\\@");
184             #endif
185              
186             AV* random_slice (av, num)
187             AV* av
188             IV num
189             PPCODE:
190              
191 2 50         if (num < 0)
192 0           croak("The slice's size can't be less than 0");
193              
194 2 50         if (num != 0) {
195              
196             static SSize_t last_index;
197              
198 2           last_index = std::move(av_top_index(av));
199 2           num -= 1;
200              
201 2 50         if (num < last_index) {
202              
203             AV *slice;
204              
205             // shuffling for usual and tied arrays
206 2           shuffle_av_first_num_elements(av, last_index, num);
207              
208 2 100         if (SvTIED_mg((SV *)av, PERL_MAGIC_tied)) {
    50          
    100          
209             static SSize_t k;
210             SV *sv, **svp;
211 1           slice = newAV();
212 5 100         for (k = 0; k <= num; k++) {
213 4           svp = av_fetch(av, k, 0);
214 4 50         sv = (svp ? newSVsv(*svp) : &PL_sv_undef);
215 4           av_push(slice, sv);
216 4           mg_set(sv);
217             }
218             }
219 1 50         else if (GIMME_V == G_VOID) {
    50          
220 0           av_fill(av, num);
221 0           XSRETURN_EMPTY;
222             }
223             else
224 1           slice = av_make(num + 1, av_fetch(av, 0, 0));
225              
226 2           ST(0) = sv_2mortal(newRV_noinc( (SV *) slice )); // mXPUSHs(newRV_noinc( (SV *) slice ));
227             }
228             }
229              
230 2           XSRETURN(1);
231              
232              
233             void shuffle (av)
234             AV *av
235             PPCODE:
236 1000012           SSize_t len = av_len(av);
237             /* it's faster than "shuffle_av_first_num_elements" */
238 1000012           shuffle_av_last_num_elements(av, len, len);
239 1000012           XSRETURN_EMPTY;
240              
241              
242             void shuffle_multi(av, ...)
243             AV* av;
244             PPCODE:
245             static SSize_t i;
246             static SSize_t len;
247             SV* sv;
248             SV *ref;
249              
250 3 50         if (items == 0)
251 0           croak("Wrong amount of arguments");
252              
253 15 100         for (i = 0; i < items; i++) {
254 12           sv = ST(i);
255 12 100         if (!SvOK(sv)) // skip undefs
    50          
    50          
256 3           continue;
257 9 50         if (!SvROK(sv)) // isn't a ref type
258 0 0         croak_sv_is_not_an_arrayref(i);
259 9           ref = SvRV(sv);
260 9 50         if (SvTYPE(ref) == SVt_PVAV) { // $ref eq "ARRAY"
261 9           av = (AV *) ref;
262 9 50         len = av_len(av);
263 9 50         shuffle_av_last_num_elements(av, len, len);
264             }
265             else // $ref ne "ARRAY"
266 0 0         croak_sv_is_not_an_arrayref(i);
267             }
268             // if (items < X) EXTEND(SP, X);
269              
270 3           XSRETURN_EMPTY;