$priv{"delete"}{64} = "SLICE";
$priv{"exists"}{64} = "SUB";
@{$priv{"sort"}}{1,2,4,8,16,32,64} = ("NUM", "INT", "REV", "INPLACE","DESC","QSORT","STABLE");
+$priv{"reverse"}{8} = "INPLACE";
$priv{"threadsv"}{64} = "SVREFd";
@{$priv{$_}}{16,32,64,128} = ("INBIN","INCR","OUTBIN","OUTCR")
for ("open", "backtick");
case OP_REVERSE: {
OP *ourmark, *theirmark, *ourlast, *iter, *expushmark, *rv2av;
OP *gvop = NULL;
+ OP *oleft, *oright;
LISTOP *enter, *exlist;
+ /* @a = reverse @a */
+ if ((oright = cLISTOPo->op_first)
+ && (oright->op_type == OP_PUSHMARK)
+ && (oright = oright->op_sibling)
+ && (oleft = is_inplace_av(o, oright))) {
+ OP *o2;
+
+ /* transfer MODishness etc from LHS arg to RHS arg */
+ oright->op_flags = oleft->op_flags;
+ o->op_private |= OPpREVERSE_INPLACE;
+
+ /* excise push->gv->rv2av->null->aassign */
+ o2 = o->op_next->op_next;
+ op_null(o2); /* PUSHMARK */
+ o2 = o2->op_next;
+ if (o2->op_type == OP_GV) {
+ op_null(o2); /* GV */
+ o2 = o2->op_next;
+ }
+ op_null(o2); /* RV2AV or PADAV */
+ o2 = o2->op_next->op_next;
+ op_null(o2); /* AASSIGN */
+
+ o->op_next = o2->op_next;
+ break;
+ }
+
enter = (LISTOP *) o->op_next;
if (!enter)
break;
#define OPpSORT_QSORT 32 /* Use quicksort (not mergesort) */
#define OPpSORT_STABLE 64 /* Use a stable algorithm */
+/* Private for OP_REVERSE */
+#define OPpREVERSE_INPLACE 8 /* reverse in-place (@a = reverse @a) */
+
/* Private for OP_OPEN and OP_BACKTICK */
#define OPpOPEN_IN_RAW 16 /* binmode(F,":raw") on input fh */
#define OPpOPEN_IN_CRLF 32 /* binmode(F,":crlf") on input fh */
=head1 Performance Enhancements
-XXX Changes which enhance performance without changing behaviour go here. There
-may well be none in a stable release.
-
=over 4
=item *
-XXX
+Reversing an array in-place in void context is now several orders of magnitude faster than it used to be.
+It will also preserve non-existent elements whenever possible, i.e. for non magical arrays or tied arrays with C<EXISTS> and C<DELETE> methods.
=back
PP(pp_reverse)
{
dVAR; dSP; dMARK;
- SV ** const oldsp = SP;
if (GIMME == G_ARRAY) {
- MARK++;
- while (MARK < SP) {
- register SV * const tmp = *MARK;
- *MARK++ = *SP;
- *SP-- = tmp;
+ if (PL_op->op_private & OPpREVERSE_INPLACE) {
+ AV *av;
+
+ /* See pp_sort() */
+ assert( MARK+1 == SP && *SP && SvTYPE(*SP) == SVt_PVAV);
+ (void)POPMARK; /* remove mark associated with ex-OP_AASSIGN */
+ av = MUTABLE_AV((*SP));
+ /* In-place reversing only happens in void context for the array
+ * assignment. We don't need to push anything on the stack. */
+ SP = MARK;
+
+ if (SvMAGICAL(av)) {
+ I32 i, j;
+ register SV *tmp = sv_newmortal();
+ /* For SvCANEXISTDELETE */
+ HV *stash;
+ const MAGIC *mg;
+ bool can_preserve = SvCANEXISTDELETE(av);
+
+ for (i = 0, j = av_len(av); i < j; ++i, --j) {
+ register SV *begin, *end;
+
+ if (can_preserve) {
+ if (!av_exists(av, i)) {
+ if (av_exists(av, j)) {
+ register SV *sv = av_delete(av, j, 0);
+ begin = *av_fetch(av, i, TRUE);
+ sv_setsv_mg(begin, sv);
+ }
+ continue;
+ }
+ else if (!av_exists(av, j)) {
+ register SV *sv = av_delete(av, i, 0);
+ end = *av_fetch(av, j, TRUE);
+ sv_setsv_mg(end, sv);
+ continue;
+ }
+ }
+
+ begin = *av_fetch(av, i, TRUE);
+ end = *av_fetch(av, j, TRUE);
+ sv_setsv(tmp, begin);
+ sv_setsv_mg(begin, end);
+ sv_setsv_mg(end, tmp);
+ }
+ }
+ else {
+ SV **begin = AvARRAY(av);
+ SV **end = begin + AvFILLp(av);
+
+ while (begin < end) {
+ register SV * const tmp = *begin;
+ *begin++ = *end;
+ *end-- = tmp;
+ }
+ }
+ }
+ else {
+ SV **oldsp = SP;
+ MARK++;
+ while (MARK < SP) {
+ register SV * const tmp = *MARK;
+ *MARK++ = *SP;
+ *SP-- = tmp;
+ }
+ /* safe as long as stack cannot get extended in the above */
+ SP = oldsp;
}
- /* safe as long as stack cannot get extended in the above */
- SP = oldsp;
}
else {
register char *up;
require './test.pl';
}
-plan tests => 5;
+plan tests => 21;
is(reverse("abc"), "cba");
}
{
+ my @a = (1, 2, 3, 4);
+ @a = reverse @a;
+ is("@a", "4 3 2 1");
+
+ delete $a[1];
+ @a = reverse @a;
+ ok(!exists $a[2]);
+ is($a[0] . $a[1] . $a[3], '124');
+
+ @a = (5, 6, 7, 8, 9);
+ @a = reverse @a;
+ is("@a", "9 8 7 6 5");
+
+ delete $a[3];
+ @a = reverse @a;
+ ok(!exists $a[1]);
+ is($a[0] . $a[2] . $a[3] . $a[4], '5789');
+
+ delete $a[2];
+ @a = reverse @a;
+ ok(!exists $a[2] && !exists $a[3]);
+ is($a[0] . $a[1] . $a[4], '985');
+}
+
+use Tie::Array;
+
+{
+ tie my @a, 'Tie::StdArray';
+
+ @a = (1, 2, 3, 4);
+ @a = reverse @a;
+ is("@a", "4 3 2 1");
+
+ delete $a[1];
+ @a = reverse @a;
+ ok(!exists $a[2]);
+ is($a[0] . $a[1] . $a[3], '124');
+
+ @a = (5, 6, 7, 8, 9);
+ @a = reverse @a;
+ is("@a", "9 8 7 6 5");
+
+ delete $a[3];
+ @a = reverse @a;
+ ok(!exists $a[1]);
+ is($a[0] . $a[2] . $a[3] . $a[4], '5789');
+
+ delete $a[2];
+ @a = reverse @a;
+ ok(!exists $a[2] && !exists $a[3]);
+ is($a[0] . $a[1] . $a[4], '985');
+}
+
+{
# Unicode.
my $a = "\x{263A}\x{263A}x\x{263A}y\x{263A}";