1 #include "EXTERN.h"
2 #include "perl.h"
3 #include "XSUB.h"
4 
5 #include "ppport.h"
6 
7 struct sort_elem {
8     SV *key;
9     SV *orig;
10 };
11 
12 static I32
sv_cmp_str_asc(pTHX_ SV * sv1,SV * sv2)13 sv_cmp_str_asc(pTHX_ SV *sv1, SV *sv2)
14 {
15     struct sort_elem *se1, *se2;
16 
17     se1 = (struct sort_elem*)SvIV(sv1);
18     se2 = (struct sort_elem*)SvIV(sv2);
19 
20     return sv_cmp_locale(se1->key, se2->key);
21 }
22 
23 static I32
sv_cmp_str_desc(pTHX_ SV * sv1,SV * sv2)24 sv_cmp_str_desc(pTHX_ SV *sv1, SV *sv2)
25 {
26     struct sort_elem *se1, *se2;
27 
28     se1 = (struct sort_elem*)SvIV(sv1);
29     se2 = (struct sort_elem*)SvIV(sv2);
30 
31     return sv_cmp_locale(se2->key, se1->key);
32 }
33 
34 static I32
sv_cmp_number_asc(pTHX_ SV * sv1,SV * sv2)35 sv_cmp_number_asc(pTHX_ SV *sv1, SV *sv2)
36 {
37     struct sort_elem *se1, *se2;
38     NV key1, key2;
39 
40     se1 = (struct sort_elem*)SvIV(sv1);
41     se2 = (struct sort_elem*)SvIV(sv2);
42 
43     key1 = SvNV(se1->key);
44     key2 = SvNV(se2->key);
45 
46     return (key1 > key2)
47            ? 1 : (key1 == key2)
48            ? 0 : -1;
49 }
50 
51 static I32
sv_cmp_number_desc(pTHX_ SV * sv1,SV * sv2)52 sv_cmp_number_desc(pTHX_ SV *sv1, SV *sv2)
53 {
54     struct sort_elem *se1, *se2;
55     NV key1, key2;
56 
57     se1 = (struct sort_elem*)SvIV(sv1);
58     se2 = (struct sort_elem*)SvIV(sv2);
59 
60     key1 = SvNV(se2->key);
61     key2 = SvNV(se1->key);
62 
63     return (key1 > key2)
64            ? 1 : (key1 == key2)
65            ? 0 : -1;
66 }
67 
68 MODULE = List::UtilsBy::XS        PACKAGE = List::UtilsBy::XS
69 
70 void
sort_by(code,...)71 sort_by (code, ...)
72     SV *code
73 PROTOTYPE: &@
74 ALIAS:
75     sort_by     = 0
76     rev_sort_by = 1
77 CODE:
78 {
79     dMULTICALL;
80     GV *gv;
81     HV *stash;
82     I32 gimme = G_SCALAR;
83     SV **args = &PL_stack_base[ax];
84     int i;
85     AV *tmps;
86     struct sort_elem *elems;
87 
88     if (items <= 1) {
89         XSRETURN_EMPTY;
90     }
91 
92     tmps = (AV *)sv_2mortal((SV *)newAV());
93 
94     cv = sv_2cv(code, &stash, &gv, 0);
95     if (cv == Nullcv) {
96        croak("Not a subroutine reference");
97     }
98 
99     PUSH_MULTICALL(cv);
100     SAVESPTR(GvSV(PL_defgv));
101 
102     Newx(elems, items - 1, struct sort_elem);
103 
104     for (i = 1; i < items; i++) {
105         struct sort_elem *elem = &elems[i - 1];
106 
107         GvSV(PL_defgv) = args[i];
108         MULTICALL;
109 
110         elem->key  = newSVsv(*PL_stack_sp);
111         elem->orig = newSVsv(args[i]);
112 
113         av_push(tmps, newSViv((IV)elem));
114     }
115 
116     POP_MULTICALL;
117 
118     if (ix) {
119         sortsv(AvARRAY(tmps), av_len(tmps) + 1, sv_cmp_str_desc);
120     } else {
121         sortsv(AvARRAY(tmps), av_len(tmps) + 1, sv_cmp_str_asc);
122     }
123 
124     for (i = 1; i < items; i++) {
125         struct sort_elem *elem;
126         elem  = (struct sort_elem *)SvIV(*av_fetch(tmps, i-1, 0));
127         ST(i-1) = sv_2mortal(elem->orig);
128         (void)sv_2mortal(elem->key);
129     }
130 
131     Safefree(elems);
132 
133     XSRETURN(items - 1);
134 }
135 
136 void
nsort_by(code,...)137 nsort_by (code, ...)
138     SV *code
139 PROTOTYPE: &@
140 ALIAS:
141     nsort_by     = 0
142     rev_nsort_by = 1
143 CODE:
144 {
145     dMULTICALL;
146     GV *gv;
147     HV *stash;
148     I32 gimme = G_SCALAR;
149     SV **args = &PL_stack_base[ax];
150     int i;
151     AV *tmps;
152     struct sort_elem *elems;
153 
154     if (items <= 1) {
155         XSRETURN_EMPTY;
156     }
157 
158     tmps = (AV *)sv_2mortal((SV *)newAV());
159 
160     cv = sv_2cv(code, &stash, &gv, 0);
161     if (cv == Nullcv) {
162        croak("Not a subroutine reference");
163     }
164 
165     PUSH_MULTICALL(cv);
166     SAVESPTR(GvSV(PL_defgv));
167 
168     Newx(elems, items - 1, struct sort_elem);
169 
170     for (i = 1; i < items; i++) {
171         struct sort_elem *elem = &elems[i - 1];
172 
173         GvSV(PL_defgv) = args[i];
174         MULTICALL;
175 
176         elem->key  = newSVsv(*PL_stack_sp);
177         elem->orig = newSVsv(args[i]);
178 
179         av_push(tmps, newSViv((IV)elem));
180     }
181 
182     POP_MULTICALL;
183 
184     if (ix) {
185         sortsv(AvARRAY(tmps), av_len(tmps) + 1, sv_cmp_number_desc);
186     } else {
187         sortsv(AvARRAY(tmps), av_len(tmps) + 1, sv_cmp_number_asc);
188     }
189 
190     for (i = 1; i < items; i++) {
191         struct sort_elem *elem;
192         elem  = (struct sort_elem *)SvIV(*av_fetch(tmps, i-1, 0));
193         ST(i-1) = sv_2mortal(elem->orig);
194         (void)sv_2mortal(elem->key);
195     }
196 
197     Safefree(elems);
198 
199     XSRETURN(items - 1);
200 }
201 
202 void
min_by(code,...)203 min_by (code, ...)
204     SV *code
205 PROTOTYPE: &@
206 ALIAS:
207     min_by = 0
208     max_by = 1
209     nmin_by = 2
210     nmax_by = 3
211 CODE:
212 {
213     dMULTICALL;
214     GV *gv;
215     HV *stash;
216     I32 gimme = G_SCALAR;
217     SV **args = &ST(1);
218     I32 const len = items - 1;
219     int i;
220     AV *tmps;
221     NV max;
222     IV ret_count = 0;
223     struct sort_elem *elems, *first;
224 
225     if (len < 1) {
226         XSRETURN_EMPTY;
227     }
228 
229     tmps = (AV *)sv_2mortal((SV *)newAV());
230 
231     cv = sv_2cv(code, &stash, &gv, 0);
232     if (cv == Nullcv) {
233        croak("Not a subroutine reference");
234     }
235 
236     PUSH_MULTICALL(cv);
237     SAVESPTR(GvSV(PL_defgv));
238 
239     Newx(elems, items - 1, struct sort_elem);
240 
241     for (i = 0; i < len; i++) {
242         struct sort_elem *elem = &elems[i];
243 
244         GvSV(PL_defgv) = args[i];
245         MULTICALL;
246 
247         elem->key  = newSVsv(*PL_stack_sp);
248         elem->orig = newSVsv(args[i]);
249 
250         av_push(tmps, newSViv((IV)elem));
251     }
252 
253     POP_MULTICALL;
254 
255     if (ix & 0x1) {
256         sortsv(AvARRAY(tmps), len, sv_cmp_number_desc);
257     } else {
258         sortsv(AvARRAY(tmps), len, sv_cmp_number_asc);
259     }
260 
261     for(i = 0; i < len; i++) {
262         struct sort_elem* elem
263             = (struct sort_elem*)SvIVx(*av_fetch(tmps, i, TRUE));
264         sv_2mortal(elem->key);
265         sv_2mortal(elem->orig);
266     }
267 
268     first = (struct sort_elem *)SvIV(*av_fetch(tmps, 0, 0));
269     max   = SvNV(first->key);
270     ST(0) = first->orig;
271     ret_count++;
272 
273     if (GIMME_V != G_ARRAY) {
274         goto ret;
275     }
276 
277     for (i = 2; i < items; i++) {
278         struct sort_elem *elem;
279         elem  = (struct sort_elem *)SvIV(*av_fetch(tmps, i-1, 0));
280 
281         if (max == SvNV(elem->key)) {
282             ST(ret_count) = elem->orig;
283             ret_count++;
284         } else {
285             goto ret;
286         }
287     }
288 
289  ret:
290     Safefree(elems);
291     XSRETURN(ret_count);
292 }
293 
294 void
uniq_by(code,...)295 uniq_by (code, ...)
296     SV *code
297 PROTOTYPE: &@
298 CODE:
299 {
300     dMULTICALL;
301     GV *gv;
302     HV *stash;
303     I32 gimme = G_SCALAR;
304     SV **args = &PL_stack_base[ax];
305     int i;
306     AV *tmps;
307     HV *rh;
308 
309     if (items <= 1) {
310         XSRETURN_EMPTY;
311     }
312 
313     tmps = (AV *)sv_2mortal((SV *)newAV());
314     rh = (HV *)sv_2mortal((SV *)newHV());
315 
316     cv = sv_2cv(code, &stash, &gv, 0);
317     if (cv == Nullcv) {
318        croak("Not a subroutine reference");
319     }
320 
321     PUSH_MULTICALL(cv);
322     SAVESPTR(GvSV(PL_defgv));
323 
324     for (i = 1; i < items; i++) {
325         STRLEN len;
326         char *str;
327 
328         GvSV(PL_defgv) = args[i];
329         MULTICALL;
330 
331         str = SvPV(*PL_stack_sp, len);
332         if (!hv_exists(rh, str, len)) {
333             av_push(tmps, newSVsv(args[i]));
334             (void)hv_store(rh, str, len, newSViv(1), 0);
335         }
336     }
337 
338     POP_MULTICALL;
339 
340     for (i = 0; i <= av_len(tmps); i++) {
341         ST(i) = *av_fetch(tmps, i, 0);
342     }
343 
344     XSRETURN(av_len(tmps) + 1);
345 }
346 
347 void
partition_by(code,...)348 partition_by (code, ...)
349     SV *code
350 PROTOTYPE: &@
351 CODE:
352 {
353     dMULTICALL;
354     GV *gv;
355     HV *stash;
356     I32 gimme = G_SCALAR;
357     SV **args = &PL_stack_base[ax];
358     int i;
359     HV *rh;
360     HE *iter = NULL;
361 
362     if (items <= 1) {
363         XSRETURN_EMPTY;
364     }
365 
366     rh = (HV *)sv_2mortal((SV *)newHV());
367 
368     cv = sv_2cv(code, &stash, &gv, 0);
369     if (cv == Nullcv) {
370        croak("Not a subroutine reference");
371     }
372 
373     PUSH_MULTICALL(cv);
374     SAVESPTR(GvSV(PL_defgv));
375 
376     for (i = 1; i < items; i++) {
377         STRLEN len;
378         char *str;
379 
380         GvSV(PL_defgv) = args[i];
381         MULTICALL;
382 
383         str = SvPV(*PL_stack_sp, len);
384         if (!hv_exists(rh, str, len)) {
385             AV* av = (AV *)sv_2mortal((SV *)newAV());
386             av_push(av, newSVsv(args[i]));
387             (void)hv_store(rh, str, len, newRV_inc((SV *)av), 0);
388         } else {
389             AV *av = (AV *)SvRV(*hv_fetch(rh, str, len, 0));
390             av_push(av, newSVsv(args[i]));
391         }
392     }
393 
394     POP_MULTICALL;
395 
396     hv_iterinit(rh);
397 
398     i = 0;
399     while ( (iter = hv_iternext( rh )) != NULL ) {
400           ST(i) = hv_iterkeysv(iter);
401           i++;
402           ST(i) = hv_iterval(rh, iter);
403           i++;
404     }
405 
406     XSRETURN(i);
407 }
408 
409 void
count_by(code,...)410 count_by (code, ...)
411     SV *code
412 PROTOTYPE: &@
413 CODE:
414 {
415     dMULTICALL;
416     GV *gv;
417     HV *stash;
418     I32 gimme = G_SCALAR;
419     SV **args = &PL_stack_base[ax];
420     int i;
421     HV *rh;
422     HE *iter = NULL;
423 
424     if (items <= 1) {
425         XSRETURN_EMPTY;
426     }
427 
428     rh = (HV *)sv_2mortal((SV *)newHV());
429 
430     cv = sv_2cv(code, &stash, &gv, 0);
431     if (cv == Nullcv) {
432        croak("Not a subroutine reference");
433     }
434 
435     PUSH_MULTICALL(cv);
436     SAVESPTR(GvSV(PL_defgv));
437 
438     for (i = 1; i < items; i++) {
439         STRLEN len;
440         char *str;
441 
442         GvSV(PL_defgv) = args[i];
443         MULTICALL;
444 
445         str = SvPV(*PL_stack_sp, len);
446         if (!hv_exists(rh, str, len)) {
447             SV* count = newSViv(1);
448             (void)hv_store(rh, str, len, count, 0);
449         } else {
450             SV **count = hv_fetch(rh, str, len, 0);
451             sv_inc(*count);
452         }
453     }
454 
455     POP_MULTICALL;
456 
457     hv_iterinit(rh);
458 
459     i = 0;
460     while ( (iter = hv_iternext( rh )) != NULL ) {
461           ST(i) = hv_iterkeysv(iter);
462           i++;
463           ST(i) = hv_iterval(rh, iter);
464           i++;
465     }
466 
467     XSRETURN(i);
468 }
469 
470 void
zip_by(code,...)471 zip_by (code, ...)
472     SV *code
473 PROTOTYPE: &@
474 CODE:
475 {
476     dSP;
477     SV **args = &PL_stack_base[ax];
478     AV *tmps, *retvals;
479     I32 i, j, count;
480     I32 len, max_length = -1;
481 
482     if (items <= 1) {
483         XSRETURN_EMPTY;
484     }
485 
486     tmps = (AV *)sv_2mortal((SV *)newAV());
487     retvals = (AV *)sv_2mortal((SV *)newAV());
488 
489     for (i = 1; i < items; i++) {
490         if (!SvROK(args[i]) || (SvTYPE(SvRV(args[i])) != SVt_PVAV)) {
491             croak("arguments should be ArrayRef");
492         }
493 
494         len = av_len((AV*)SvRV(args[i]));
495         if (len > max_length) {
496             max_length = len;
497         }
498 
499         av_push(tmps, newSVsv(args[i]));
500     }
501 
502     SAVESPTR(GvSV(PL_defgv));
503 
504     for (i = 0; i <= max_length; i++) {
505         ENTER;
506         SAVETMPS;
507 
508         PUSHMARK(sp);
509         for (j = 1; j < items; j++) {
510             AV *av = (AV*)SvRV( *av_fetch(tmps, j-1, 0) );
511 
512             if (av_exists(av, i)) {
513                 SV *elem = *av_fetch(av, i, 0);
514                 XPUSHs(sv_2mortal(newSVsv(elem)));
515             } else {
516                 XPUSHs(&PL_sv_undef);
517             }
518         }
519         PUTBACK;
520 
521         count = call_sv(code, G_ARRAY);
522 
523         SPAGAIN;
524 
525         len = av_len(retvals);
526         for (j = 0; j < count; j++) {
527             av_store(retvals, len + (count - j), newSVsv(POPs));
528         }
529 
530         PUTBACK;
531         FREETMPS;
532         LEAVE;
533     }
534 
535     len = av_len(retvals) + 1;
536     for (i = 0; i < len; i++) {
537         ST(i) = *av_fetch(retvals, i, 0);
538     }
539 
540     XSRETURN(len);
541 }
542 
543 void
unzip_by(code,...)544 unzip_by (code, ...)
545     SV *code
546 PROTOTYPE: &@
547 CODE:
548 {
549     dSP;
550     SV **args = &PL_stack_base[ax];
551     AV *retvals;
552     I32 i, j, count;
553     I32 len, max_len = 0;
554 
555     if (items <= 1) {
556         XSRETURN_EMPTY;
557     }
558 
559     retvals = (AV *)sv_2mortal((SV *)newAV());
560 
561     SAVESPTR(GvSV(PL_defgv));
562 
563     for (i = 1; i < items; i++) {
564         ENTER;
565         SAVETMPS;
566 
567         PUSHMARK(sp);
568         XPUSHs(sv_2mortal(newSVsv(args[i])));
569         PUTBACK;
570 
571         GvSV(PL_defgv) = args[i];
572         count = call_sv(code, G_ARRAY);
573 
574         SPAGAIN;
575 
576         for (j = max_len; j < count; j++) {
577             AV *tmp = (AV *)sv_2mortal((SV *)newAV());
578             av_store(retvals, j, newRV((SV*)tmp));
579         }
580 
581         if (max_len < count) {
582             max_len = count;
583         }
584 
585         for (j = count - 1; j >= 0; j--) {
586             SV *ret  = newSVsv(POPs);
587             AV *tmp = (AV *)SvRV((SV*)*av_fetch(retvals, j, 0));
588             av_store(tmp, i - 1, ret);
589         }
590 
591         PUTBACK;
592         FREETMPS;
593         LEAVE;
594     }
595 
596     len = av_len(retvals) + 1;
597     for (i = 0; i < len; i++) {
598         AV *tmp = (AV *)SvRV((SV*)*av_fetch(retvals, i, 0));
599         for (j = av_len(tmp) + 1; j < (items - 1); j++) {
600             av_push(tmp, &PL_sv_undef);
601         }
602     }
603 
604     for (i = 0; i < len; i++) {
605         ST(i) = *av_fetch(retvals, i, 0);
606     }
607 
608     XSRETURN(len);
609 }
610 
611 void
extract_by(code,...)612 extract_by (code, ...)
613     SV *code
614 PROTOTYPE: &\@
615 CODE:
616 {
617     dMULTICALL;
618     GV *gv;
619     HV *stash;
620     I32 gimme = G_SCALAR, ret_gimme = GIMME_V;
621     SV **args = &PL_stack_base[ax];
622     IV i, len;
623     AV *ret_vals, *remains, *origs;
624 
625     if (items <= 1) {
626         XSRETURN_EMPTY;
627     }
628 
629     ret_vals = (AV *)sv_2mortal((SV *)newAV());
630     remains  = (AV *)sv_2mortal((SV *)newAV());
631 
632     cv = sv_2cv(code, &stash, &gv, 0);
633     if (cv == Nullcv) {
634        croak("Not a subroutine reference");
635     }
636 
637     if (!SvROK(args[1]) || (SvTYPE(SvRV(args[1])) != SVt_PVAV)) {
638         croak("arguments should be ArrayRef");
639     }
640 
641     origs = (AV*)SvRV(args[1]);
642     len = av_len((AV*)SvRV(args[1])) + 1;
643 
644     PUSH_MULTICALL(cv);
645     SAVESPTR(GvSV(PL_defgv));
646 
647     for (i = 0; i < len; i++) {
648         SV *val, *arg;
649 
650         arg = *av_fetch(origs, i, 0);
651         GvSV(PL_defgv) = arg;
652         MULTICALL;
653 
654         val = newSVsv(*PL_stack_sp);
655         if (SvTRUE(val)) {
656             av_push(ret_vals, newSVsv(arg));
657         } else {
658             SV *val = newSVsv(arg);
659             SvFLAGS(val) = SvFLAGS(arg);
660             av_push(remains, val);
661         }
662     }
663 
664     POP_MULTICALL;
665 
666     av_clear(origs);
667 
668     len = av_len(remains) + 1;
669     for (i = 0; i < len; i++) {
670         SV *val = *av_fetch(remains, i, 0);
671         av_push(origs, newSVsv(val));
672     }
673 
674     if (ret_gimme == G_SCALAR) {
675         len = 1;
676         ST(0) = sv_2mortal(newSViv(av_len(ret_vals)+1));
677     } else {
678         len = av_len(ret_vals) + 1;
679         for (i = 0; i < len; i++) {
680             ST(i) = sv_mortalcopy(*av_fetch(ret_vals, i, 0));
681         }
682     }
683 
684     XSRETURN(len);
685 }
686 
687 void
weighted_shuffle_by(code,...)688 weighted_shuffle_by (code, ...)
689     SV *code
690 PROTOTYPE: &@
691 CODE:
692 {
693     dMULTICALL;
694     GV *gv;
695     HV *stash;
696     I32 gimme = G_SCALAR;
697     SV **args = &PL_stack_base[ax];
698     I32 i, len;
699     AV *weights, *origs, *retvals;
700 
701     if (items <= 1) {
702         XSRETURN_EMPTY;
703     }
704 
705     weights = (AV *)sv_2mortal((SV *)newAV());
706     origs   = (AV *)sv_2mortal((SV *)newAV());
707     retvals = (AV *)sv_2mortal((SV *)newAV());
708 
709     cv = sv_2cv(code, &stash, &gv, 0);
710     if (cv == Nullcv) {
711        croak("Not a subroutine reference");
712     }
713 
714     PUSH_MULTICALL(cv);
715     SAVESPTR(GvSV(PL_defgv));
716 
717     for (i = 1; i < items; i++) {
718         av_push(origs, newSVsv(args[i]));
719 
720         GvSV(PL_defgv) = args[i];
721         MULTICALL;
722 
723         av_push(weights, newSVsv(*PL_stack_sp));
724     }
725 
726     POP_MULTICALL;
727 
728     /* Initialize Drand01 if rand() or srand() has
729        not already been called
730     */
731     if (!PL_srand_called) {
732         (void)seedDrand01((Rand_seed_t)seed());
733         PL_srand_called = TRUE;
734     }
735 
736     while ( (av_len(origs) + 1) > 1) {
737         IV total = 0;
738         I32 select;
739         I32 idx;
740         SV *selected, *last;
741 
742         len = av_len(weights) + 1;
743         for (i = 0; i < len; i++) {
744             total += SvIV(*av_fetch(weights, i, 0));
745         }
746 
747         select = (I32)(Drand01() * (double)total);
748         idx = 0;
749         while (select >= SvIV(*av_fetch(weights, idx, 0))) {
750             select -= SvIV(*av_fetch(weights, idx, 0));
751 
752             if (av_len(weights) > idx) {
753                 idx++;
754             } else {
755                 break;
756             }
757         }
758 
759         selected = *av_fetch(origs, idx, 0);
760         av_push(retvals, newSVsv(selected));
761 
762         last = *av_fetch(origs, av_len(origs), 0);
763         av_store(origs, idx, last);
764         (void)av_pop(origs);
765 
766         last = *av_fetch(weights, av_len(weights), 0);
767         av_store(weights, idx, last);
768         (void)av_pop(weights);
769     }
770 
771     len = av_len(origs) + 1;
772     for (i = 0 ; i < len; i++) {
773         av_push(retvals, av_shift(origs));
774     }
775 
776     for (i = 1 ; i < items; i++) {
777         ST(i-1) = sv_2mortal(newSVsv( *av_fetch(retvals, i-1, 0) ));
778     }
779 
780     XSRETURN(items-1);
781 }
782 
783 void
bundle_by(code,...)784 bundle_by (code, ...)
785     SV *code
786 PROTOTYPE: &@
787 CODE:
788 {
789     dSP;
790     SV **args = &PL_stack_base[ax];
791     AV *retvals;
792     IV argnum;
793     I32 i, j, count, len, loop;
794 
795     if (items <= 1) {
796         XSRETURN_EMPTY;
797     }
798 
799     argnum = SvIV(args[1]);
800     if (argnum <= 0) {
801         croak("bundle number is larger than 0");
802     }
803 
804     retvals = (AV *)sv_2mortal((SV *)newAV());
805 
806     SAVESPTR(GvSV(PL_defgv));
807 
808     for (i = 2, loop = 0; i < items; i += argnum, loop++) {
809         ENTER;
810         SAVETMPS;
811 
812         PUSHMARK(sp);
813         for (j = 0; j < argnum; j++) {
814             I32 index = (loop * argnum) + j + 2;
815             if (SvOK(args[index])) {
816                 XPUSHs(sv_2mortal(newSVsv(args[index])));
817             } else {
818                 XPUSHs(&PL_sv_undef);
819             }
820         }
821         PUTBACK;
822 
823         count = call_sv(code, G_ARRAY);
824 
825         SPAGAIN;
826 
827         len = av_len(retvals);
828         for (j = 0; j < count; j++) {
829             av_store(retvals, len + (count - j), newSVsv(POPs));
830         }
831 
832         PUTBACK;
833         FREETMPS;
834         LEAVE;
835     }
836 
837     len = av_len(retvals) + 1;
838     for (i = 0; i < len; i++) {
839         ST(i) = *av_fetch(retvals, i, 0);
840     }
841 
842     XSRETURN(len);
843 }
844