File Coverage

XS.xs
Criterion Covered Total %
statement 0 679 0.0
branch 0 780 0.0
condition n/a
subroutine n/a
pod n/a
total 0 1459 0.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 "multicall.h"
6             #define NEED_croak_xs_usage
7             #include "ppport.h"
8              
9             #ifndef aTHX
10             # define aTHX
11             # define pTHX
12             #endif
13              
14             #ifdef SVf_IVisUV
15             # define slu_sv_value(sv) (SvIOK(sv)) ? (SvIOK_UV(sv)) ? (NV)(SvUVX(sv)) : (NV)(SvIVX(sv)) : (SvNV(sv))
16             #else
17             # define slu_sv_value(sv) (SvIOK(sv)) ? (NV)(SvIVX(sv)) : (SvNV(sv))
18             #endif
19              
20             /*
21             * Perl < 5.18 had some kind of different SvIV_please_nomg
22             */
23             #if PERL_VERSION < 18
24             #undef SvIV_please_nomg
25             # define SvIV_please_nomg(sv) \
26             (!SvIOKp(sv) && (SvNOK(sv) || SvPOK(sv)) \
27             ? (SvIV_nomg(sv), SvIOK(sv)) \
28             : SvIOK(sv))
29             #endif
30              
31             /* compare left and right SVs. Returns:
32             * -1: <
33             * 0: ==
34             * 1: >
35             * 2: left or right was a NaN
36             */
37             static I32
38 0           LSUXSncmp(pTHX_ SV* left, SV * right)
39             {
40             /* Fortunately it seems NaN isn't IOK */
41 0 0         if (SvAMAGIC(left) || SvAMAGIC(right))
    0          
    0          
    0          
    0          
    0          
42 0           return SvIVX(amagic_call(left, right, ncmp_amg, 0));
43              
44 0 0         if (SvIV_please_nomg(right) && SvIV_please_nomg(left)) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
45 0 0         if (!SvUOK(left)) {
46 0           const IV leftiv = SvIVX(left);
47 0 0         if (!SvUOK(right)) {
48             /* ## IV <=> IV ## */
49 0           const IV rightiv = SvIVX(right);
50 0           return (leftiv > rightiv) - (leftiv < rightiv);
51             }
52             /* ## IV <=> UV ## */
53 0 0         if (leftiv < 0)
54             /* As (b) is a UV, it's >=0, so it must be < */
55 0           return -1;
56             {
57 0           const UV rightuv = SvUVX(right);
58 0           return ((UV)leftiv > rightuv) - ((UV)leftiv < rightuv);
59             }
60             }
61              
62 0 0         if (SvUOK(right)) {
63             /* ## UV <=> UV ## */
64 0           const UV leftuv = SvUVX(left);
65 0           const UV rightuv = SvUVX(right);
66 0           return (leftuv > rightuv) - (leftuv < rightuv);
67             }
68             /* ## UV <=> IV ## */
69             {
70 0           const IV rightiv = SvIVX(right);
71 0 0         if (rightiv < 0)
72             /* As (a) is a UV, it's >=0, so it cannot be < */
73 0           return 1;
74             {
75 0           const UV leftuv = SvUVX(left);
76 0           return (leftuv > (UV)rightiv) - (leftuv < (UV)rightiv);
77             }
78             }
79             assert(0); /* NOTREACHED */
80             } else {
81             #ifdef SvNV_nomg
82 0           NV const rnv = SvNV_nomg(right);
83 0           NV const lnv = SvNV_nomg(left);
84             #else
85             NV const rnv = slu_sv_value(right);
86             NV const lnv = slu_sv_value(left);
87             #endif
88              
89             #if defined(NAN_COMPARE_BROKEN) && defined(Perl_isnan)
90             if (Perl_isnan(lnv) || Perl_isnan(rnv)) {
91             return 2;
92             }
93             return (lnv > rnv) - (lnv < rnv);
94             #else
95 0 0         if (lnv < rnv)
96 0           return -1;
97 0 0         if (lnv > rnv)
98 0           return 1;
99 0 0         if (lnv == rnv)
100 0           return 0;
101 0           return 2;
102             #endif
103             }
104             }
105              
106             #define ncmp(left,right) LSUXSncmp(aTHX_ left,right)
107              
108             #define FUNC_NAME GvNAME(GvEGV(ST(items)))
109              
110             /* shameless stolen from PadWalker */
111             #ifndef PadARRAY
112             typedef AV PADNAMELIST;
113             typedef SV PADNAME;
114             # if PERL_VERSION < 8 || (PERL_VERSION == 8 && !PERL_SUBVERSION)
115             typedef AV PADLIST;
116             typedef AV PAD;
117             # endif
118             # define PadlistARRAY(pl) ((PAD **)AvARRAY(pl))
119             # define PadlistMAX(pl) AvFILLp(pl)
120             # define PadlistNAMES(pl) (*PadlistARRAY(pl))
121             # define PadnamelistARRAY(pnl) ((PADNAME **)AvARRAY(pnl))
122             # define PadnamelistMAX(pnl) AvFILLp(pnl)
123             # define PadARRAY AvARRAY
124             # define PadnameIsOUR(pn) !!(SvFLAGS(pn) & SVpad_OUR)
125             # define PadnameOURSTASH(pn) SvOURSTASH(pn)
126             # define PadnameOUTER(pn) !!SvFAKE(pn)
127             # define PadnamePV(pn) (SvPOKp(pn) ? SvPVX(pn) : NULL)
128             #endif
129             #ifndef PadnameSV
130             # define PadnameSV(pn) pn
131             #endif
132             #ifndef PadnameFLAGS
133             # define PadnameFLAGS(pn) (SvFLAGS(PadnameSV(pn)))
134             #endif
135              
136             static int
137 0           in_pad (pTHX_ SV *code)
138             {
139             GV *gv;
140             HV *stash;
141 0           CV *cv = sv_2cv(code, &stash, &gv, 0);
142 0           PADLIST *pad_list = (CvPADLIST(cv));
143 0           PADNAMELIST *pad_namelist = PadlistNAMES(pad_list);
144             int i;
145              
146 0 0         for (i=PadnamelistMAX(pad_namelist); i>=0; --i) {
147 0           PADNAME* name_pn = PadnamelistARRAY(pad_namelist)[i];
148 0 0         if (name_pn) {
149 0           char *name_str = PadnamePV(name_pn);
150 0 0         if (name_str) {
151              
152 0 0         if (PadnameIsOUR(name_pn))
153 0           continue;
154              
155 0 0         if (strEQ(name_str, "$a") || strEQ(name_str, "$b"))
    0          
156 0           return 1;
157              
158 0 0         if (!((PadnameFLAGS(name_pn)) & SVf_OK))
159 0           continue;
160             }
161             }
162             }
163 0           return 0;
164             }
165              
166             #define WARN_OFF \
167             SV *oldwarn = PL_curcop->cop_warnings; \
168             PL_curcop->cop_warnings = pWARN_NONE;
169              
170             #define WARN_ON \
171             PL_curcop->cop_warnings = oldwarn;
172              
173             #define EACH_ARRAY_BODY \
174             int i; \
175             arrayeach_args * args; \
176             HV *stash = gv_stashpv("List::SomeUtils_ea", TRUE); \
177             CV *closure = newXS(NULL, XS_List__SomeUtils__XS__array_iterator, __FILE__); \
178             \
179             /* prototype */ \
180             sv_setpv((SV*)closure, ";$"); \
181             \
182             New(0, args, 1, arrayeach_args); \
183             New(0, args->avs, items, AV*); \
184             args->navs = items; \
185             args->curidx = 0; \
186             \
187             for (i = 0; i < items; i++) { \
188             if (!arraylike(ST(i))) \
189             croak_xs_usage(cv, "\\@;\\@\\@..."); \
190             args->avs[i] = (AV*)SvRV(ST(i)); \
191             SvREFCNT_inc(args->avs[i]); \
192             } \
193             \
194             CvXSUBANY(closure).any_ptr = args; \
195             RETVAL = newRV_noinc((SV*)closure); \
196             \
197             /* in order to allow proper cleanup in DESTROY-handler */ \
198             sv_bless(RETVAL, stash)
199              
200             #define FOR_EACH(on_item) \
201             if (!codelike(code)) \
202             croak_xs_usage(cv, "code, ..."); \
203             \
204             if (items > 1) { \
205             dMULTICALL; \
206             int i; \
207             HV *stash; \
208             GV *gv; \
209             CV *_cv; \
210             SV **args = &PL_stack_base[ax]; \
211             I32 gimme = G_SCALAR; \
212             _cv = sv_2cv(code, &stash, &gv, 0); \
213             PUSH_MULTICALL(_cv); \
214             SAVESPTR(GvSV(PL_defgv)); \
215             \
216             for (i = 1 ; i < items ; ++i) { \
217             GvSV(PL_defgv) = args[i]; \
218             MULTICALL; \
219             on_item; \
220             } \
221             POP_MULTICALL; \
222             }
223              
224             #define TRUE_JUNCTION \
225             FOR_EACH(if (SvTRUE(*PL_stack_sp)) ON_TRUE) \
226             else ON_EMPTY;
227              
228             #define FALSE_JUNCTION \
229             FOR_EACH(if (!SvTRUE(*PL_stack_sp)) ON_FALSE) \
230             else ON_EMPTY;
231              
232             /* need this one for array_each() */
233             typedef struct {
234             AV **avs; /* arrays over which to iterate in parallel */
235             int navs; /* number of arrays */
236             int curidx; /* the current index of the iterator */
237             } arrayeach_args;
238              
239             /* used for natatime */
240             typedef struct {
241             SV **svs;
242             int nsvs;
243             int curidx;
244             int natatime;
245             } natatime_args;
246              
247             static void
248 0           insert_after (pTHX_ int idx, SV *what, AV *av) {
249             int i, len;
250 0           av_extend(av, (len = av_len(av) + 1));
251              
252 0 0         for (i = len; i > idx+1; i--) {
253 0           SV **sv = av_fetch(av, i-1, FALSE);
254 0           SvREFCNT_inc(*sv);
255 0           av_store(av, i, *sv);
256             }
257 0 0         if (!av_store(av, idx+1, what))
258 0           SvREFCNT_dec(what);
259 0           }
260              
261             static int
262 0           is_like(pTHX_ SV *sv, const char *like)
263             {
264 0           int likely = 0;
265 0 0         if ( sv_isobject( sv ) )
266             {
267 0           dSP;
268             int count;
269              
270 0           ENTER;
271 0           SAVETMPS;
272 0 0         PUSHMARK(SP);
273 0 0         XPUSHs( sv_2mortal( newSVsv( sv ) ) );
274 0 0         XPUSHs( sv_2mortal( newSVpv( like, strlen(like) ) ) );
275 0           PUTBACK;
276              
277 0 0         if ( ( count = call_pv("overload::Method", G_SCALAR) ) ) {
278             I32 ax;
279 0           SPAGAIN;
280              
281 0           SP -= count;
282 0           ax = (SP - PL_stack_base) + 1;
283 0 0         if ( SvTRUE(ST(0)) )
284 0           ++likely;
285             }
286              
287 0           PUTBACK;
288 0 0         FREETMPS;
289 0           LEAVE;
290             }
291              
292 0           return likely;
293             }
294              
295             static int
296 0           is_array(SV *sv)
297             {
298 0 0         return SvROK(sv) && ( SVt_PVAV == SvTYPE(SvRV(sv) ) );
    0          
299             }
300              
301             static int
302 0           LSUXScodelike(pTHX_ SV *code)
303             {
304 0 0         SvGETMAGIC(code);
    0          
305 0 0         return SvROK(code) && ( ( SVt_PVCV == SvTYPE(SvRV(code)) ) || ( is_like(aTHX_ code, "&{}" ) ) );
    0          
    0          
306             }
307              
308             #define codelike(code) LSUXScodelike(aTHX_ code)
309              
310             static int
311 0           LSUXSarraylike(pTHX_ SV *array)
312             {
313 0 0         SvGETMAGIC(array);
    0          
314 0 0         return is_array(array) || is_like(aTHX_ array, "@{}" );
    0          
315             }
316              
317             #define arraylike(array) LSUXSarraylike(aTHX_ array)
318              
319             MODULE = List::SomeUtils_ea PACKAGE = List::SomeUtils_ea
320              
321             void
322             DESTROY(sv)
323             SV *sv;
324             CODE:
325             {
326             int i;
327 0           CV *code = (CV*)SvRV(sv);
328 0           arrayeach_args *args = (arrayeach_args *)(CvXSUBANY(code).any_ptr);
329 0 0         if (args) {
330 0 0         for (i = 0; i < args->navs; ++i)
331 0           SvREFCNT_dec(args->avs[i]);
332 0           Safefree(args->avs);
333 0           Safefree(args);
334 0           CvXSUBANY(code).any_ptr = NULL;
335             }
336             }
337              
338              
339             MODULE = List::SomeUtils_na PACKAGE = List::SomeUtils_na
340              
341             void
342             DESTROY(sv)
343             SV *sv;
344             CODE:
345             {
346             int i;
347 0           CV *code = (CV*)SvRV(sv);
348 0           natatime_args *args = (natatime_args *)(CvXSUBANY(code).any_ptr);
349 0 0         if (args) {
350 0 0         for (i = 0; i < args->nsvs; ++i)
351 0           SvREFCNT_dec(args->svs[i]);
352 0           Safefree(args->svs);
353 0           Safefree(args);
354 0           CvXSUBANY(code).any_ptr = NULL;
355             }
356             }
357              
358             MODULE = List::SomeUtils::XS PACKAGE = List::SomeUtils::XS
359              
360             void
361             any (code,...)
362             SV *code;
363             PROTOTYPE: &@
364             CODE:
365             {
366             #define ON_TRUE { POP_MULTICALL; XSRETURN_YES; }
367             #define ON_EMPTY XSRETURN_NO
368 0 0         TRUE_JUNCTION;
    0          
    0          
    0          
    0          
    0          
    0          
369 0           XSRETURN_NO;
370             #undef ON_EMPTY
371             #undef ON_TRUE
372             }
373              
374             void
375             all (code, ...)
376             SV *code;
377             PROTOTYPE: &@
378             CODE:
379             {
380             #define ON_FALSE { POP_MULTICALL; XSRETURN_NO; }
381             #define ON_EMPTY XSRETURN_YES
382 0 0         FALSE_JUNCTION;
    0          
    0          
    0          
    0          
    0          
    0          
383 0           XSRETURN_YES;
384             #undef ON_EMPTY
385             #undef ON_FALSE
386             }
387              
388              
389             void
390             none (code, ...)
391             SV *code;
392             PROTOTYPE: &@
393             CODE:
394             {
395             #define ON_TRUE { POP_MULTICALL; XSRETURN_NO; }
396             #define ON_EMPTY XSRETURN_YES
397 0 0         TRUE_JUNCTION;
    0          
    0          
    0          
    0          
    0          
    0          
398 0           XSRETURN_YES;
399             #undef ON_EMPTY
400             #undef ON_TRUE
401             }
402              
403             void
404             notall (code, ...)
405             SV *code;
406             PROTOTYPE: &@
407             CODE:
408             {
409             #define ON_FALSE { POP_MULTICALL; XSRETURN_YES; }
410             #define ON_EMPTY XSRETURN_NO
411 0 0         FALSE_JUNCTION;
    0          
    0          
    0          
    0          
    0          
    0          
412 0           XSRETURN_NO;
413             #undef ON_EMPTY
414             #undef ON_FALSE
415             }
416              
417             void
418             one (code, ...)
419             SV *code;
420             PROTOTYPE: &@
421             CODE:
422             {
423 0           int found = 0;
424             #define ON_TRUE { if (found++) { POP_MULTICALL; XSRETURN_NO; }; }
425             #define ON_EMPTY XSRETURN_NO
426 0 0         TRUE_JUNCTION;
    0          
    0          
    0          
    0          
    0          
    0          
    0          
427 0 0         if (found)
428 0           XSRETURN_YES;
429 0           XSRETURN_NO;
430             #undef ON_EMPTY
431             #undef ON_TRUE
432             }
433              
434             void
435             any_u (code,...)
436             SV *code;
437             PROTOTYPE: &@
438             CODE:
439             {
440             #define ON_TRUE { POP_MULTICALL; XSRETURN_YES; }
441             #define ON_EMPTY XSRETURN_UNDEF
442 0 0         TRUE_JUNCTION;
    0          
    0          
    0          
    0          
    0          
    0          
443 0           XSRETURN_NO;
444             #undef ON_EMPTY
445             #undef ON_TRUE
446             }
447              
448             void
449             all_u (code, ...)
450             SV *code;
451             PROTOTYPE: &@
452             CODE:
453             {
454             #define ON_FALSE { POP_MULTICALL; XSRETURN_NO; }
455             #define ON_EMPTY XSRETURN_UNDEF
456 0 0         FALSE_JUNCTION;
    0          
    0          
    0          
    0          
    0          
    0          
457 0           XSRETURN_YES;
458             #undef ON_EMPTY
459             #undef ON_FALSE
460             }
461              
462              
463             void
464             none_u (code, ...)
465             SV *code;
466             PROTOTYPE: &@
467             CODE:
468             {
469             #define ON_TRUE { POP_MULTICALL; XSRETURN_NO; }
470             #define ON_EMPTY XSRETURN_UNDEF
471 0 0         TRUE_JUNCTION;
    0          
    0          
    0          
    0          
    0          
    0          
472 0           XSRETURN_YES;
473             #undef ON_EMPTY
474             #undef ON_TRUE
475             }
476              
477             void
478             notall_u (code, ...)
479             SV *code;
480             PROTOTYPE: &@
481             CODE:
482             {
483             #define ON_FALSE { POP_MULTICALL; XSRETURN_YES; }
484             #define ON_EMPTY XSRETURN_UNDEF
485 0 0         FALSE_JUNCTION;
    0          
    0          
    0          
    0          
    0          
    0          
486 0           XSRETURN_NO;
487             #undef ON_EMPTY
488             #undef ON_FALSE
489             }
490              
491             void
492             one_u (code, ...)
493             SV *code;
494             PROTOTYPE: &@
495             CODE:
496             {
497 0           int found = 0;
498             #define ON_TRUE { if (found++) { POP_MULTICALL; XSRETURN_NO; }; }
499             #define ON_EMPTY XSRETURN_UNDEF
500 0 0         TRUE_JUNCTION;
    0          
    0          
    0          
    0          
    0          
    0          
    0          
501 0 0         if (found)
502 0           XSRETURN_YES;
503 0           XSRETURN_NO;
504             #undef ON_EMPTY
505             #undef ON_TRUE
506             }
507              
508             int
509             true (code, ...)
510             SV *code;
511             PROTOTYPE: &@
512             CODE:
513             {
514 0           I32 count = 0;
515 0 0         FOR_EACH(if (SvTRUE(*PL_stack_sp)) count++);
    0          
    0          
    0          
    0          
    0          
516 0 0         RETVAL = count;
517             }
518             OUTPUT:
519             RETVAL
520              
521             int
522             false (code, ...)
523             SV *code;
524             PROTOTYPE: &@
525             CODE:
526             {
527 0           I32 count = 0;
528 0 0         FOR_EACH(if (!SvTRUE(*PL_stack_sp)) count++);
    0          
    0          
    0          
    0          
    0          
529 0 0         RETVAL = count;
530             }
531             OUTPUT:
532             RETVAL
533              
534             int
535             firstidx (code, ...)
536             SV *code;
537             PROTOTYPE: &@
538             CODE:
539             {
540 0           RETVAL = -1;
541 0 0         FOR_EACH(if (SvTRUE(*PL_stack_sp)) { RETVAL = i-1; break; });
    0          
    0          
    0          
    0          
    0          
542             }
543             OUTPUT:
544             RETVAL
545              
546             SV *
547             firstval (code, ...)
548             SV *code;
549             PROTOTYPE: &@
550             CODE:
551             {
552 0           RETVAL = &PL_sv_undef;
553 0 0         FOR_EACH(if (SvTRUE(*PL_stack_sp)) { SvREFCNT_inc(RETVAL = args[i]); break; });
    0          
    0          
    0          
    0          
    0          
554             }
555             OUTPUT:
556             RETVAL
557              
558             SV *
559             firstres (code, ...)
560             SV *code;
561             PROTOTYPE: &@
562             CODE:
563             {
564 0           RETVAL = &PL_sv_undef;
565 0 0         FOR_EACH(if (SvTRUE(*PL_stack_sp)) { SvREFCNT_inc(RETVAL = *PL_stack_sp); break; });
    0          
    0          
    0          
    0          
    0          
566             }
567             OUTPUT:
568             RETVAL
569              
570             int
571             onlyidx (code, ...)
572             SV *code;
573             PROTOTYPE: &@
574             CODE:
575             {
576 0           int found = 0;
577 0           RETVAL = -1;
578 0 0         FOR_EACH(if (SvTRUE(*PL_stack_sp)) { if (found++) {RETVAL = -1; break;} RETVAL = i-1; });
    0          
    0          
    0          
    0          
    0          
    0          
579             }
580             OUTPUT:
581             RETVAL
582              
583             SV *
584             onlyval (code, ...)
585             SV *code;
586             PROTOTYPE: &@
587             CODE:
588             {
589 0           int found = 0;
590 0           RETVAL = &PL_sv_undef;
591 0 0         FOR_EACH(if (SvTRUE(*PL_stack_sp)) { if (found++) {SvREFCNT_dec(RETVAL); RETVAL = &PL_sv_undef; break;} SvREFCNT_inc(RETVAL = args[i]); });
    0          
    0          
    0          
    0          
    0          
    0          
592             }
593             OUTPUT:
594             RETVAL
595              
596             SV *
597             onlyres (code, ...)
598             SV *code;
599             PROTOTYPE: &@
600             CODE:
601             {
602 0           int found = 0;
603 0           RETVAL = &PL_sv_undef;
604 0 0         FOR_EACH(if (SvTRUE(*PL_stack_sp)) { if (found++) {SvREFCNT_dec(RETVAL); RETVAL = &PL_sv_undef; break;}SvREFCNT_inc(RETVAL = *PL_stack_sp); });
    0          
    0          
    0          
    0          
    0          
    0          
605             }
606             OUTPUT:
607             RETVAL
608              
609             int
610             lastidx (code, ...)
611             SV *code;
612             PROTOTYPE: &@
613             CODE:
614             {
615             dMULTICALL;
616             int i;
617             HV *stash;
618             GV *gv;
619 0           I32 gimme = G_SCALAR;
620 0           SV **args = &PL_stack_base[ax];
621             CV *_cv;
622              
623 0 0         if (!codelike(code))
624 0           croak_xs_usage(cv, "code, ...");
625              
626 0           RETVAL = -1;
627              
628 0 0         if (items > 1) {
629 0           _cv = sv_2cv(code, &stash, &gv, 0);
630 0 0         PUSH_MULTICALL(_cv);
631 0           SAVESPTR(GvSV(PL_defgv));
632              
633 0 0         for (i = items-1 ; i > 0 ; --i) {
634 0           GvSV(PL_defgv) = args[i];
635 0           MULTICALL;
636 0 0         if (SvTRUE(*PL_stack_sp)) {
637 0           RETVAL = i-1;
638 0           break;
639             }
640             }
641 0 0         POP_MULTICALL;
642             }
643             }
644             OUTPUT:
645             RETVAL
646              
647             SV *
648             lastval (code, ...)
649             SV *code;
650             PROTOTYPE: &@
651             CODE:
652             {
653             dMULTICALL;
654             int i;
655             HV *stash;
656             GV *gv;
657 0           I32 gimme = G_SCALAR;
658 0           SV **args = &PL_stack_base[ax];
659             CV *_cv;
660              
661 0           RETVAL = &PL_sv_undef;
662              
663 0 0         if (!codelike(code))
664 0           croak_xs_usage(cv, "code, ...");
665              
666 0 0         if (items > 1) {
667 0           _cv = sv_2cv(code, &stash, &gv, 0);
668 0 0         PUSH_MULTICALL(_cv);
669 0           SAVESPTR(GvSV(PL_defgv));
670              
671 0 0         for (i = items-1 ; i > 0 ; --i) {
672 0           GvSV(PL_defgv) = args[i];
673 0           MULTICALL;
674 0 0         if (SvTRUE(*PL_stack_sp)) {
675             /* see comment in indexes() */
676 0           SvREFCNT_inc(RETVAL = args[i]);
677 0           break;
678             }
679             }
680 0 0         POP_MULTICALL;
681             }
682             }
683             OUTPUT:
684             RETVAL
685              
686             SV *
687             lastres (code, ...)
688             SV *code;
689             PROTOTYPE: &@
690             CODE:
691             {
692             dMULTICALL;
693             int i;
694             HV *stash;
695             GV *gv;
696 0           I32 gimme = G_SCALAR;
697 0           SV **args = &PL_stack_base[ax];
698             CV *_cv;
699              
700 0           RETVAL = &PL_sv_undef;
701              
702 0 0         if (!codelike(code))
703 0           croak_xs_usage(cv, "code, ...");
704              
705 0 0         if (items > 1) {
706 0           _cv = sv_2cv(code, &stash, &gv, 0);
707 0 0         PUSH_MULTICALL(_cv);
708 0           SAVESPTR(GvSV(PL_defgv));
709              
710 0 0         for (i = items-1 ; i > 0 ; --i) {
711 0           GvSV(PL_defgv) = args[i];
712 0           MULTICALL;
713 0 0         if (SvTRUE(*PL_stack_sp)) {
714             /* see comment in indexes() */
715 0           SvREFCNT_inc(RETVAL = *PL_stack_sp);
716 0           break;
717             }
718             }
719 0 0         POP_MULTICALL;
720             }
721             }
722             OUTPUT:
723             RETVAL
724              
725             int
726             insert_after (code, val, avref)
727             SV *code;
728             SV *val;
729             SV *avref;
730             PROTOTYPE: &$\@
731             CODE:
732             {
733             dMULTICALL;
734             int i;
735             int len;
736             HV *stash;
737             GV *gv;
738 0           I32 gimme = G_SCALAR;
739             CV *_cv;
740             AV *av;
741              
742 0 0         if (!codelike(code))
743 0           croak_xs_usage(cv, "code, val, \\@area_of_operation");
744 0 0         if (!arraylike(avref))
745 0           croak_xs_usage(cv, "code, val, \\@area_of_operation");
746              
747 0           av = (AV*)SvRV(avref);
748 0           len = av_len(av);
749 0           RETVAL = 0;
750              
751 0           _cv = sv_2cv(code, &stash, &gv, 0);
752 0 0         PUSH_MULTICALL(_cv);
753 0           SAVESPTR(GvSV(PL_defgv));
754              
755 0 0         for (i = 0; i <= len ; ++i) {
756 0           GvSV(PL_defgv) = *av_fetch(av, i, FALSE);
757 0           MULTICALL;
758 0 0         if (SvTRUE(*PL_stack_sp)) {
759 0           RETVAL = 1;
760 0           break;
761             }
762             }
763              
764 0 0         POP_MULTICALL;
765              
766 0 0         if (RETVAL) {
767 0           SvREFCNT_inc(val);
768 0           insert_after(aTHX_ i, val, av);
769             }
770             }
771             OUTPUT:
772             RETVAL
773              
774             int
775             insert_after_string (string, val, avref)
776             SV *string;
777             SV *val;
778             SV *avref;
779             PROTOTYPE: $$\@
780             CODE:
781             {
782             int i;
783             AV *av;
784             int len;
785             SV **sv;
786 0           STRLEN slen = 0, alen;
787             char *str;
788             char *astr;
789 0           RETVAL = 0;
790              
791 0 0         if (!arraylike(avref))
792 0           croak_xs_usage(cv, "string, val, \\@area_of_operation");
793              
794 0           av = (AV*)SvRV(avref);
795 0           len = av_len(av);
796              
797 0 0         if (SvTRUE(string))
798 0           str = SvPV(string, slen);
799             else
800 0           str = NULL;
801              
802 0 0         for (i = 0; i <= len ; i++) {
803 0           sv = av_fetch(av, i, FALSE);
804 0 0         if (SvTRUE(*sv))
805 0           astr = SvPV(*sv, alen);
806             else {
807 0           astr = NULL;
808 0           alen = 0;
809             }
810 0 0         if (slen == alen && memcmp(astr, str, slen) == 0) {
    0          
811 0           RETVAL = 1;
812 0           break;
813             }
814             }
815 0 0         if (RETVAL) {
816 0           SvREFCNT_inc(val);
817 0           insert_after(aTHX_ i, val, av);
818             }
819             }
820             OUTPUT:
821             RETVAL
822              
823             void
824             apply (code, ...)
825             SV *code;
826             PROTOTYPE: &@
827             CODE:
828             {
829             dMULTICALL;
830             int i;
831             HV *stash;
832             GV *gv;
833 0           I32 gimme = G_SCALAR;
834             CV *_cv;
835 0           SV **args = &PL_stack_base[ax];
836              
837 0 0         if (!codelike(code))
838 0           croak_xs_usage(cv, "code, ...");
839              
840 0 0         if (items <= 1)
841 0           XSRETURN_EMPTY;
842              
843 0           _cv = sv_2cv(code, &stash, &gv, 0);
844 0 0         PUSH_MULTICALL(_cv);
845 0           SAVESPTR(GvSV(PL_defgv));
846              
847 0 0         for (i = 1 ; i < items ; ++i) {
848 0           GvSV(PL_defgv) = newSVsv(args[i]);
849 0           MULTICALL;
850 0           args[i-1] = GvSV(PL_defgv);
851             }
852 0 0         POP_MULTICALL;
853              
854 0 0         for (i = 1 ; i < items ; ++i)
855 0           sv_2mortal(args[i-1]);
856              
857 0           XSRETURN(items-1);
858             }
859              
860             void
861             after (code, ...)
862             SV *code;
863             PROTOTYPE: &@
864             CODE:
865             {
866             dMULTICALL;
867             int i, j;
868             HV *stash;
869             CV *_cv;
870             GV *gv;
871 0           I32 gimme = G_SCALAR;
872 0           SV **args = &PL_stack_base[ax];
873              
874 0 0         if (!codelike(code))
875 0           croak_xs_usage(cv, "code, ...");
876              
877 0 0         if (items <= 1)
878 0           XSRETURN_EMPTY;
879              
880 0           _cv = sv_2cv(code, &stash, &gv, 0);
881 0 0         PUSH_MULTICALL(_cv);
882 0           SAVESPTR(GvSV(PL_defgv));
883              
884 0 0         for (i = 1; i < items; i++) {
885 0           GvSV(PL_defgv) = args[i];
886 0           MULTICALL;
887 0 0         if (SvTRUE(*PL_stack_sp)) {
888 0           break;
889             }
890             }
891              
892 0 0         POP_MULTICALL;
893              
894 0 0         for (j = i + 1; j < items; ++j)
895 0           args[j-i-1] = args[j];
896              
897 0           j = items-i-1;
898 0           XSRETURN(j > 0 ? j : 0);
899             }
900              
901             void
902             after_incl (code, ...)
903             SV *code;
904             PROTOTYPE: &@
905             CODE:
906             {
907             dMULTICALL;
908             int i, j;
909             HV *stash;
910             CV *_cv;
911             GV *gv;
912 0           I32 gimme = G_SCALAR;
913 0           SV **args = &PL_stack_base[ax];
914              
915 0 0         if (!codelike(code))
916 0           croak_xs_usage(cv, "code, ...");
917              
918 0 0         if (items <= 1)
919 0           XSRETURN_EMPTY;
920              
921 0           _cv = sv_2cv(code, &stash, &gv, 0);
922 0 0         PUSH_MULTICALL(_cv);
923 0           SAVESPTR(GvSV(PL_defgv));
924              
925 0 0         for (i = 1; i < items; i++) {
926 0           GvSV(PL_defgv) = args[i];
927 0           MULTICALL;
928 0 0         if (SvTRUE(*PL_stack_sp)) {
929 0           break;
930             }
931             }
932              
933 0 0         POP_MULTICALL;
934              
935 0 0         for (j = i; j < items; j++)
936 0           args[j-i] = args[j];
937              
938 0           XSRETURN(items-i);
939             }
940              
941             void
942             before (code, ...)
943             SV *code;
944             PROTOTYPE: &@
945             CODE:
946             {
947             dMULTICALL;
948             int i;
949             HV *stash;
950             GV *gv;
951 0           I32 gimme = G_SCALAR;
952 0           SV **args = &PL_stack_base[ax];
953             CV *_cv;
954              
955 0 0         if (!codelike(code))
956 0           croak_xs_usage(cv, "code, ...");
957              
958 0 0         if (items <= 1)
959 0           XSRETURN_EMPTY;
960              
961 0           _cv = sv_2cv(code, &stash, &gv, 0);
962 0 0         PUSH_MULTICALL(_cv);
963 0           SAVESPTR(GvSV(PL_defgv));
964              
965 0 0         for (i = 1; i < items; i++) {
966 0           GvSV(PL_defgv) = args[i];
967 0           MULTICALL;
968 0 0         if (SvTRUE(*PL_stack_sp)) {
969 0           break;
970             }
971 0           args[i-1] = args[i];
972             }
973              
974 0 0         POP_MULTICALL;
975              
976 0           XSRETURN(i-1);
977             }
978              
979             void
980             before_incl (code, ...)
981             SV *code;
982             PROTOTYPE: &@
983             CODE:
984             {
985             dMULTICALL;
986             int i;
987             HV *stash;
988             GV *gv;
989 0           I32 gimme = G_SCALAR;
990 0           SV **args = &PL_stack_base[ax];
991             CV *_cv;
992              
993 0 0         if (!codelike(code))
994 0           croak_xs_usage(cv, "code, ...");
995              
996 0 0         if (items <= 1)
997 0           XSRETURN_EMPTY;
998              
999 0           _cv = sv_2cv(code, &stash, &gv, 0);
1000 0 0         PUSH_MULTICALL(_cv);
1001 0           SAVESPTR(GvSV(PL_defgv));
1002              
1003 0 0         for (i = 1; i < items; ++i) {
1004 0           GvSV(PL_defgv) = args[i];
1005 0           MULTICALL;
1006 0           args[i-1] = args[i];
1007 0 0         if (SvTRUE(*PL_stack_sp)) {
1008 0           ++i;
1009 0           break;
1010             }
1011             }
1012              
1013 0 0         POP_MULTICALL;
1014              
1015 0           XSRETURN(i-1);
1016             }
1017              
1018             void
1019             indexes (code, ...)
1020             SV *code;
1021             PROTOTYPE: &@
1022             CODE:
1023             {
1024             dMULTICALL;
1025             int i, j;
1026             HV *stash;
1027             GV *gv;
1028 0           I32 gimme = G_SCALAR;
1029 0           SV **args = &PL_stack_base[ax];
1030             CV *_cv;
1031              
1032 0 0         if (!codelike(code))
1033 0           croak_xs_usage(cv, "code, ...");
1034              
1035 0 0         if (items <= 1)
1036 0           XSRETURN_EMPTY;
1037              
1038 0           _cv = sv_2cv(code, &stash, &gv, 0);
1039 0 0         PUSH_MULTICALL(_cv);
1040 0           SAVESPTR(GvSV(PL_defgv));
1041              
1042 0 0         for (i = 1, j = 0; i < items; i++) {
1043 0           GvSV(PL_defgv) = args[i];
1044 0           MULTICALL;
1045 0 0         if (SvTRUE(*PL_stack_sp))
1046             /* POP_MULTICALL can free mortal temporaries, so we defer
1047             * mortalising the returned values till after that's been done */
1048 0           args[j++] = newSViv(i-1);
1049             }
1050              
1051 0 0         POP_MULTICALL;
1052              
1053 0 0         for (i = 0; i < j; i++)
1054 0           sv_2mortal(args[i]);
1055              
1056 0           XSRETURN(j);
1057             }
1058              
1059             void
1060             _array_iterator (method = "")
1061             const char *method;
1062             PROTOTYPE: ;$
1063             CODE:
1064             {
1065             int i;
1066 0           int exhausted = 1;
1067              
1068             /* 'cv' is the hidden argument with which
1069             * XS_List__SomeUtils__XS__array_iterator (this XSUB) is called. The
1070             * closure_arg struct is stored in this CV. */
1071              
1072 0           arrayeach_args *args = (arrayeach_args *)(CvXSUBANY(cv).any_ptr);
1073              
1074 0 0         if (strEQ(method, "index")) {
1075 0 0         EXTEND(SP, 1);
1076 0 0         ST(0) = args->curidx > 0 ? sv_2mortal(newSViv(args->curidx-1)) : &PL_sv_undef;
1077 0           XSRETURN(1);
1078             }
1079              
1080 0 0         EXTEND(SP, args->navs);
    0          
1081              
1082 0 0         for (i = 0; i < args->navs; i++) {
1083 0           AV *av = args->avs[i];
1084 0 0         if (args->curidx <= av_len(av)) {
1085 0           ST(i) = sv_2mortal(newSVsv(*av_fetch(av, args->curidx, FALSE)));
1086 0           exhausted = 0;
1087 0           continue;
1088             }
1089 0           ST(i) = &PL_sv_undef;
1090             }
1091              
1092 0 0         if (exhausted)
1093 0           XSRETURN_EMPTY;
1094              
1095 0           args->curidx++;
1096 0           XSRETURN(args->navs);
1097             }
1098              
1099             SV *
1100             each_array (...)
1101             PROTOTYPE: \@;\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@
1102             CODE:
1103             {
1104 0 0         EACH_ARRAY_BODY;
    0          
1105             }
1106             OUTPUT:
1107             RETVAL
1108              
1109             SV *
1110             each_arrayref (...)
1111             CODE:
1112             {
1113 0 0         EACH_ARRAY_BODY;
    0          
1114             }
1115             OUTPUT:
1116             RETVAL
1117              
1118             void
1119             pairwise (code, ...)
1120             SV *code;
1121             PROTOTYPE: &\@\@
1122             PPCODE:
1123             {
1124             #define av_items(a) (av_len(a)+1)
1125              
1126             /* This function is not quite as efficient as it ought to be: We call
1127             * 'code' multiple times and want to gather its return values all in one
1128             * list. However, each call resets the stack pointer so there is no
1129             * obvious way to get the return values onto the stack without making
1130             * intermediate copies of the pointers. The above disabled solution would
1131             * be more efficient. Unfortunately it doesn't work (and, as of now,
1132             * wouldn't deal with 'code' returning more than one value).
1133             *
1134             * The current solution is a fair trade-off. It only allocates memory for
1135             * a list of SV-pointers, as many as there are return values. It
1136             * temporarily stores 'code's return values in this list and, when done,
1137             * copies them down to SP. */
1138              
1139             int i, j;
1140             AV *avs[2];
1141             SV **buf, **p; /* gather return values here and later copy down to SP */
1142             int alloc;
1143              
1144 0           int nitems = 0, maxitems = 0;
1145             int d;
1146              
1147 0 0         if (!codelike(code))
1148 0           croak_xs_usage(cv, "code, list, list");
1149 0 0         if (!arraylike(ST(1)))
1150 0           croak_xs_usage(cv, "code, list, list");
1151 0 0         if (!arraylike(ST(2)))
1152 0           croak_xs_usage(cv, "code, list, list");
1153              
1154 0 0         if (in_pad(aTHX_ code)) {
1155 0           croak("Can't use lexical $a or $b in pairwise code block");
1156             }
1157              
1158             /* deref AV's for convenience and
1159             * get maximum items */
1160 0           avs[0] = (AV*)SvRV(ST(1));
1161 0           avs[1] = (AV*)SvRV(ST(2));
1162 0           maxitems = av_items(avs[0]);
1163 0 0         if (av_items(avs[1]) > maxitems)
1164 0           maxitems = av_items(avs[1]);
1165              
1166 0 0         if (!PL_firstgv || !PL_secondgv) {
    0          
1167 0           SAVESPTR(PL_firstgv);
1168 0           SAVESPTR(PL_secondgv);
1169 0           PL_firstgv = gv_fetchpv("a", TRUE, SVt_PV);
1170 0           PL_secondgv = gv_fetchpv("b", TRUE, SVt_PV);
1171             }
1172              
1173 0           New(0, buf, alloc = maxitems, SV*);
1174              
1175 0           ENTER;
1176 0 0         for (d = 0, i = 0; i < maxitems; i++) {
1177             int nret;
1178 0           SV **svp = av_fetch(avs[0], i, FALSE);
1179 0 0         GvSV(PL_firstgv) = svp ? *svp : &PL_sv_undef;
1180 0           svp = av_fetch(avs[1], i, FALSE);
1181 0 0         GvSV(PL_secondgv) = svp ? *svp : &PL_sv_undef;
1182 0 0         PUSHMARK(SP);
1183 0           PUTBACK;
1184 0           nret = call_sv(code, G_EVAL|G_ARRAY);
1185 0 0         if (SvTRUE(ERRSV)) {
    0          
1186 0           Safefree(buf);
1187 0 0         croak("%s", SvPV_nolen(ERRSV));
1188             }
1189 0           SPAGAIN;
1190 0           nitems += nret;
1191 0 0         if (nitems > alloc) {
1192 0 0         while (nitems > alloc) {
1193 0           alloc <<= 2;
1194             }
1195 0           Renew(buf, alloc, SV*);
1196             }
1197 0 0         for (j = nret-1; j >= 0; j--) {
1198             /* POPs would return elements in reverse order */
1199 0           buf[d] = sp[-j];
1200 0           d++;
1201             }
1202 0           sp -= nret;
1203             }
1204 0           LEAVE;
1205 0 0         EXTEND(SP, nitems);
    0          
1206 0           p = buf;
1207 0 0         for (i = 0; i < nitems; i++)
1208 0           ST(i) = *p++;
1209              
1210 0           Safefree(buf);
1211 0           XSRETURN(nitems);
1212             }
1213              
1214             void
1215             _natatime_iterator ()
1216             PROTOTYPE:
1217             CODE:
1218             {
1219             int i;
1220             int nret;
1221              
1222             /* 'cv' is the hidden argument with which
1223             * XS_List__SomeUtils__XS__array_iterator (this XSUB) is called. The
1224             * closure_arg struct is stored in this CV. */
1225              
1226 0           natatime_args *args = (natatime_args*)CvXSUBANY(cv).any_ptr;
1227              
1228 0           nret = args->natatime;
1229              
1230 0 0         EXTEND(SP, nret);
    0          
1231              
1232 0 0         for (i = 0; i < args->natatime; i++) {
1233 0 0         if (args->curidx < args->nsvs) {
1234 0           ST(i) = sv_2mortal(newSVsv(args->svs[args->curidx++]));
1235             }
1236             else {
1237 0           XSRETURN(i);
1238             }
1239             }
1240              
1241 0           XSRETURN(nret);
1242             }
1243              
1244             SV *
1245             natatime (n, ...)
1246             int n;
1247             PROTOTYPE: $@
1248             CODE:
1249             {
1250             int i;
1251             natatime_args * args;
1252 0           HV *stash = gv_stashpv("List::SomeUtils_na", TRUE);
1253              
1254 0           CV *closure = newXS(NULL, XS_List__SomeUtils__XS__natatime_iterator, __FILE__);
1255              
1256             /* must NOT set prototype on iterator: otherwise one cannot write: &$it */
1257             /* !! sv_setpv((SV*)closure, ""); !! */
1258              
1259 0           New(0, args, 1, natatime_args);
1260 0           New(0, args->svs, items-1, SV*);
1261 0           args->nsvs = items-1;
1262 0           args->curidx = 0;
1263 0           args->natatime = n;
1264              
1265 0 0         for (i = 1; i < items; i++)
1266 0           SvREFCNT_inc(args->svs[i-1] = ST(i));
1267              
1268 0           CvXSUBANY(closure).any_ptr = args;
1269 0           RETVAL = newRV_noinc((SV*)closure);
1270              
1271             /* in order to allow proper cleanup in DESTROY-handler */
1272 0           sv_bless(RETVAL, stash);
1273             }
1274             OUTPUT:
1275             RETVAL
1276              
1277             void
1278             mesh (...)
1279             PROTOTYPE: \@\@;\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@
1280             CODE:
1281             {
1282 0           int i, j, maxidx = -1;
1283             AV **avs;
1284 0           New(0, avs, items, AV*);
1285              
1286 0 0         for (i = 0; i < items; i++) {
1287 0 0         if (!arraylike(ST(i)))
1288 0           croak_xs_usage(cv, "\\@;\\@\\@...");
1289 0           avs[i] = (AV*)SvRV(ST(i));
1290 0 0         if (av_len(avs[i]) > maxidx)
1291 0           maxidx = av_len(avs[i]);
1292             }
1293              
1294 0 0         EXTEND(SP, items * (maxidx + 1));
    0          
1295 0 0         for (i = 0; i <= maxidx; i++) {
1296 0 0         for (j = 0; j < items; j++) {
1297 0           SV **svp = av_fetch(avs[j], i, FALSE);
1298 0 0         ST(i*items + j) = svp ? sv_2mortal(newSVsv(*svp)) : &PL_sv_undef;
1299             }
1300             }
1301              
1302 0           Safefree(avs);
1303 0           XSRETURN(items * (maxidx + 1));
1304             }
1305              
1306             void
1307             uniq (...)
1308             PROTOTYPE: @
1309             CODE:
1310             {
1311             I32 i;
1312 0           IV count = 0, seen_undef = 0;
1313 0           HV *hv = newHV();
1314 0           SV **args = &PL_stack_base[ax];
1315 0           SV *tmp = sv_newmortal();
1316 0           sv_2mortal(newRV_noinc((SV*)hv));
1317              
1318             /* don't build return list in scalar context */
1319 0 0         if (GIMME_V == G_SCALAR) {
1320 0 0         for (i = 0; i < items; i++) {
1321 0 0         SvGETMAGIC(args[i]);
    0          
1322 0 0         if (SvOK(args[i])) {
1323 0           sv_setsv_nomg(tmp, args[i]);
1324 0 0         if (!hv_exists_ent(hv, tmp, 0)) {
1325 0           ++count;
1326 0           hv_store_ent(hv, tmp, &PL_sv_yes, 0);
1327             }
1328             }
1329 0 0         else if (0 == seen_undef++) {
1330 0           ++count;
1331             }
1332             }
1333 0           ST(0) = sv_2mortal(newSVuv(count));
1334 0           XSRETURN(1);
1335             }
1336              
1337             /* list context: populate SP with mortal copies */
1338 0 0         for (i = 0; i < items; i++) {
1339 0 0         SvGETMAGIC(args[i]);
    0          
1340 0 0         if (SvOK(args[i])) {
1341 0 0         SvSetSV_nosteal(tmp, args[i]);
1342 0 0         if (!hv_exists_ent(hv, tmp, 0)) {
1343             /*ST(count) = sv_2mortal(newSVsv(ST(i)));
1344             ++count;*/
1345 0           args[count++] = args[i];
1346 0           hv_store_ent(hv, tmp, &PL_sv_yes, 0);
1347             }
1348             }
1349 0 0         else if (0 == seen_undef++) {
1350 0           args[count++] = args[i];
1351             }
1352             }
1353              
1354 0           XSRETURN(count);
1355             }
1356              
1357             void
1358             singleton (...)
1359             PROTOTYPE: @
1360             CODE:
1361             {
1362             I32 i;
1363 0           IV cnt = 0, count = 0, seen_undef = 0;
1364 0           HV *hv = newHV();
1365 0           SV **args = &PL_stack_base[ax];
1366 0           SV *tmp = sv_newmortal();
1367              
1368 0           sv_2mortal(newRV_noinc((SV*)hv));
1369              
1370 0 0         for (i = 0; i < items; i++) {
1371 0 0         SvGETMAGIC(args[i]);
    0          
1372 0 0         if (SvOK(args[i])) {
1373             HE *he;
1374 0 0         SvSetSV_nosteal(tmp, args[i]);
1375 0           he = hv_fetch_ent(hv, tmp, 0, 0);
1376 0 0         if (NULL == he) {
1377             /* ST(count) = sv_2mortal(newSVsv(ST(i))); */
1378 0           args[count++] = args[i];
1379 0           hv_store_ent(hv, tmp, newSViv(1), 0);
1380             }
1381             else {
1382 0           SV *v = HeVAL(he);
1383 0           IV how_many = SvIVX(v);
1384 0           sv_setiv(v, ++how_many);
1385             }
1386             }
1387 0 0         else if (0 == seen_undef++) {
1388 0           args[count++] = args[i];
1389             }
1390             }
1391              
1392             /* don't build return list in scalar context */
1393 0 0         if (GIMME_V == G_SCALAR) {
1394 0 0         for (i = 0; i < count; i++) {
1395 0 0         if (SvOK(args[i])) {
1396             HE *he;
1397 0           sv_setsv_nomg(tmp, args[i]);
1398 0           he = hv_fetch_ent(hv, tmp, 0, 0);
1399 0 0         if (he) {
1400 0           SV *v = HeVAL(he);
1401 0           IV how_many = SvIVX(v);
1402 0 0         if ( 1 == how_many )
1403 0           ++cnt;
1404             }
1405             }
1406 0 0         else if (1 == seen_undef) {
1407 0           ++cnt;
1408             }
1409             }
1410 0           ST(0) = sv_2mortal(newSViv(cnt));
1411 0           XSRETURN(1);
1412             }
1413              
1414             /* list context: populate SP with mortal copies */
1415 0 0         for (i = 0; i < count; i++) {
1416 0 0         if (SvOK(args[i])) {
1417             HE *he;
1418 0 0         SvSetSV_nosteal(tmp, args[i]);
1419 0           he = hv_fetch_ent(hv, tmp, 0, 0);
1420 0 0         if (he) {
1421 0           SV *v = HeVAL(he);
1422 0           IV how_many = SvIVX(v);
1423 0 0         if ( 1 == how_many )
1424 0           args[cnt++] = args[i];
1425             }
1426             }
1427 0 0         else if (1 == seen_undef) {
1428 0           args[cnt++] = args[i];
1429             }
1430             }
1431              
1432 0           XSRETURN(cnt);
1433             }
1434              
1435             void
1436             minmax (...)
1437             PROTOTYPE: @
1438             CODE:
1439             {
1440             I32 i;
1441             SV *minsv, *maxsv;
1442              
1443 0 0         if (!items)
1444 0           XSRETURN_EMPTY;
1445              
1446 0 0         if (items == 1) {
1447 0 0         EXTEND(SP, 1);
1448 0           ST(1) = sv_2mortal(newSVsv(ST(0)));
1449 0           XSRETURN(2);
1450             }
1451              
1452 0           minsv = maxsv = ST(0);
1453              
1454 0 0         for (i = 1; i < items; i += 2) {
1455 0           SV *asv = ST(i-1);
1456 0           SV *bsv = ST(i);
1457 0           int cmp = ncmp(asv, bsv);
1458 0 0         if (cmp < 0) {
1459 0           int min_cmp = ncmp(minsv, asv);
1460 0           int max_cmp = ncmp(maxsv, bsv);
1461 0 0         if (min_cmp > 0) {
1462 0           minsv = asv;
1463             }
1464 0 0         if (max_cmp < 0) {
1465 0           maxsv = bsv;
1466             }
1467             } else {
1468 0           int min_cmp = ncmp(minsv, bsv);
1469 0           int max_cmp = ncmp(maxsv, asv);
1470 0 0         if (min_cmp > 0) {
1471 0           minsv = bsv;
1472             }
1473 0 0         if (max_cmp < 0) {
1474 0           maxsv = asv;
1475             }
1476             }
1477             }
1478              
1479 0 0         if (items & 1) {
1480 0           SV *rsv = ST(items-1);
1481 0 0         if (ncmp(minsv, rsv) > 0) {
1482 0           minsv = rsv;
1483             }
1484 0 0         else if (ncmp(maxsv, rsv) < 0) {
1485 0           maxsv = rsv;
1486             }
1487             }
1488              
1489 0           ST(0) = minsv;
1490 0           ST(1) = maxsv;
1491              
1492 0           XSRETURN(2);
1493             }
1494              
1495             void
1496             part (code, ...)
1497             SV *code;
1498             PROTOTYPE: &@
1499             CODE:
1500             {
1501             dMULTICALL;
1502             int i;
1503             HV *stash;
1504             GV *gv;
1505 0           I32 gimme = G_SCALAR;
1506 0           SV **args = &PL_stack_base[ax];
1507             CV *_cv;
1508              
1509 0           AV **tmp = NULL;
1510 0           int last = 0;
1511              
1512 0 0         if (!codelike(code))
1513 0           croak_xs_usage(cv, "code, ...");
1514              
1515 0 0         if (items == 1)
1516 0           XSRETURN_EMPTY;
1517              
1518 0           _cv = sv_2cv(code, &stash, &gv, 0);
1519 0 0         PUSH_MULTICALL(_cv);
1520 0           SAVESPTR(GvSV(PL_defgv));
1521              
1522 0 0         for (i = 1 ; i < items ; ++i) {
1523             int idx;
1524 0           GvSV(PL_defgv) = args[i];
1525 0           MULTICALL;
1526 0           idx = SvIV(*PL_stack_sp);
1527              
1528 0 0         if (idx < 0 && (idx += last) < 0)
    0          
1529 0           croak("Modification of non-creatable array value attempted, subscript %i", idx);
1530              
1531 0 0         if (idx >= last) {
1532 0           int oldlast = last;
1533 0           last = idx + 1;
1534 0           Renew(tmp, last, AV*);
1535 0           Zero(tmp + oldlast, last - oldlast, AV*);
1536             }
1537 0 0         if (!tmp[idx])
1538 0           tmp[idx] = newAV();
1539 0           av_push(tmp[idx], newSVsv( args[i] ));
1540             }
1541 0 0         POP_MULTICALL;
1542              
1543 0 0         EXTEND(SP, last);
    0          
1544 0 0         for (i = 0; i < last; ++i) {
1545 0 0         if (tmp[i])
1546 0           ST(i) = sv_2mortal(newRV_noinc((SV*)tmp[i]));
1547             else
1548 0           ST(i) = &PL_sv_undef;
1549             }
1550              
1551 0           Safefree(tmp);
1552 0           XSRETURN(last);
1553             }
1554              
1555             SV *
1556             bsearch (code, ...)
1557             SV *code;
1558             PROTOTYPE: &@
1559             CODE:
1560             {
1561             dMULTICALL;
1562             HV *stash;
1563             GV *gv;
1564 0           I32 gimme = GIMME_V; /* perl-5.5.4 bus-errors out later when using GIMME
1565             therefore we save its value in a fresh variable */
1566 0           SV **args = &PL_stack_base[ax];
1567              
1568             long i, j;
1569 0           int val = -1;
1570              
1571 0 0         if (!codelike(code))
1572 0           croak_xs_usage(cv, "code, ...");
1573              
1574 0 0         if (items > 1) {
1575 0           CV *_cv = sv_2cv(code, &stash, &gv, 0);
1576 0 0         PUSH_MULTICALL(_cv);
1577 0           SAVESPTR(GvSV(PL_defgv));
1578              
1579 0           i = 0;
1580 0           j = items - 1;
1581             do {
1582 0           long k = (i + j) / 2;
1583              
1584 0 0         if (k >= items-1)
1585 0           break;
1586              
1587 0           GvSV(PL_defgv) = args[1+k];
1588 0           MULTICALL;
1589 0           val = SvIV(*PL_stack_sp);
1590              
1591 0 0         if (val == 0) {
1592 0 0         POP_MULTICALL;
1593 0 0         if (gimme != G_ARRAY) {
1594 0           XSRETURN_YES;
1595             }
1596 0           SvREFCNT_inc(RETVAL = args[1+k]);
1597 0           goto yes;
1598             }
1599 0 0         if (val < 0) {
1600 0           i = k+1;
1601             } else {
1602 0           j = k-1;
1603             }
1604 0 0         } while (i <= j);
1605 0 0         POP_MULTICALL;
1606             }
1607              
1608 0 0         if (gimme == G_ARRAY)
1609 0           XSRETURN_EMPTY;
1610             else
1611 0           XSRETURN_UNDEF;
1612 0           yes:
1613             ;
1614             }
1615             OUTPUT:
1616             RETVAL
1617              
1618             int
1619             bsearchidx (code, ...)
1620             SV *code;
1621             PROTOTYPE: &@
1622             CODE:
1623             {
1624             dMULTICALL;
1625             HV *stash;
1626             GV *gv;
1627 0           I32 gimme = GIMME_V; /* perl-5.5.4 bus-errors out later when using GIMME
1628             therefore we save its value in a fresh variable */
1629 0           SV **args = &PL_stack_base[ax];
1630              
1631             long i, j;
1632 0           int val = -1;
1633              
1634 0 0         if (!codelike(code))
1635 0           croak_xs_usage(cv, "code, ...");
1636              
1637 0           RETVAL = -1;
1638              
1639 0 0         if (items > 1) {
1640 0           CV *_cv = sv_2cv(code, &stash, &gv, 0);
1641 0 0         PUSH_MULTICALL(_cv);
1642 0           SAVESPTR(GvSV(PL_defgv));
1643              
1644 0           i = 0;
1645 0           j = items - 1;
1646             do {
1647 0           long k = (i + j) / 2;
1648              
1649 0 0         if (k >= items-1)
1650 0           break;
1651              
1652 0           GvSV(PL_defgv) = args[1+k];
1653 0           MULTICALL;
1654 0           val = SvIV(*PL_stack_sp);
1655              
1656 0 0         if (val == 0) {
1657 0           RETVAL = k;
1658 0           break;
1659             }
1660 0 0         if (val < 0) {
1661 0           i = k+1;
1662             } else {
1663 0           j = k-1;
1664             }
1665 0 0         } while (i <= j);
1666 0 0         POP_MULTICALL;
1667             }
1668             }
1669             OUTPUT:
1670             RETVAL
1671              
1672             void
1673             mode (...)
1674             PROTOTYPE: @
1675             PPCODE:
1676             {
1677             int i;
1678 0           unsigned int max = 0;
1679 0           unsigned int c = 0;
1680 0           unsigned int modality = 0;
1681 0           SV **args = &PL_stack_base[ax];
1682 0           HV *hv = newHV();
1683 0           SV *tmp = sv_newmortal();
1684             HE *he;
1685              
1686 0           sv_2mortal(newRV_noinc((SV*)hv));
1687 0 0         if (!items) {
1688 0 0         if (GIMME_V == G_SCALAR) {
1689 0           mPUSHi(0);
1690 0           PUTBACK;
1691 0           return;
1692             }
1693             else {
1694 0           XSRETURN_EMPTY;
1695             }
1696             }
1697              
1698 0 0         for (i = 0; i < items; i++) {
1699 0 0         SvGETMAGIC(args[i]);
    0          
1700              
1701 0 0         SvSetSV_nosteal(tmp, args[i]);
1702 0           he = hv_fetch_ent(hv, tmp, 0, 0);
1703              
1704 0 0         if (NULL == he) {
1705 0           hv_store_ent(hv, tmp, newSViv(1), 0);
1706             }
1707             else {
1708 0           SV *v = HeVAL(he);
1709 0           IV how_many = SvIVX(v);
1710 0           sv_setiv(v, ++how_many);
1711             }
1712             }
1713              
1714 0           hv_iterinit(hv);
1715 0 0         while ((he = hv_iternext(hv))) {
1716 0           c = SvIV(HeVAL(he));
1717 0 0         if (c > max) {
1718 0           max = c;
1719             }
1720             }
1721              
1722 0           i = 0;
1723 0           hv_iterinit(hv);
1724 0 0         while ((he = hv_iternext(hv))) {
1725 0 0         if (SvIV(HeVAL(he)) == max) {
1726 0 0         if (GIMME_V == G_SCALAR) {
1727 0           modality++;
1728             } else {
1729 0 0         XPUSHs(HeSVKEY_force(he));
    0          
    0          
1730             }
1731             }
1732             }
1733              
1734 0 0         if (GIMME_V == G_SCALAR) {
1735 0 0         mXPUSHu(modality);
1736             }
1737             }