perl/perl-5.31.8-pp_sort.c-fix-f...

143 lines
4.7 KiB
Diff

From 2b301921ff7682e54ab74ad30dbf2ce1c9fc24b1 Mon Sep 17 00:00:00 2001
From: Yves Orton <demerphq@gmail.com>
Date: Fri, 31 Jan 2020 15:34:48 +0100
Subject: [PATCH] pp_sort.c: fix fencepost error in call to av_extend()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
In [rt.cpan.org #39196] issue #17496 there is a report
that Tie::File produced spurious blank lines in the file
after
@tied= sort @tied;
it turns out that this is because Tie::File treats
EXTEND similarly to STORESIZE (which is arguably not
entirely correct, but also not that weird) coupled
with an off by one error in the calls to av_extend()
in pp_sort.
This patch fixes the fencepost error, adds some comments
to av_extend() to make it clear what it is doing, and
adds a test that EXTEND is called by this code with
correct argument.
Signed-off-by: Petr Písař <ppisar@redhat.com>
---
av.c | 18 ++++++++++++++++--
pp_sort.c | 5 +++--
t/op/sort.t | 23 +++++++++++++++++++++--
3 files changed, 40 insertions(+), 6 deletions(-)
diff --git a/av.c b/av.c
index 918844c376..27b2f12032 100644
--- a/av.c
+++ b/av.c
@@ -55,8 +55,13 @@ Perl_av_reify(pTHX_ AV *av)
/*
=for apidoc av_extend
-Pre-extend an array. The C<key> is the index to which the array should be
-extended.
+Pre-extend an array so that it is capable of storing values at indexes
+C<0..key>. Thus C<av_extend(av,99)> guarantees that the array can store 100
+elements, i.e. that C<av_store(av, 0, sv)> through C<av_store(av, 99, sv)>
+on a plain array will work without any further memory allocation.
+
+If the av argument is a tied array then will call the C<EXTEND> tied
+array method with an argument of C<(key+1)>.
=cut
*/
@@ -72,6 +77,15 @@ Perl_av_extend(pTHX_ AV *av, SSize_t key)
mg = SvTIED_mg((const SV *)av, PERL_MAGIC_tied);
if (mg) {
SV *arg1 = sv_newmortal();
+ /* NOTE: the API for av_extend() is NOT the same as the tie method EXTEND.
+ *
+ * The C function takes an *index* (assumes 0 indexed arrays) and ensures
+ * that the array is at least as large as the index provided.
+ *
+ * The tied array method EXTEND takes a *count* and ensures that the array
+ * is at least that many elements large. Thus we have to +1 the key when
+ * we call the tied method.
+ */
sv_setiv(arg1, (IV)(key + 1));
Perl_magic_methcall(aTHX_ MUTABLE_SV(av), mg, SV_CONST(EXTEND), G_DISCARD, 1,
arg1);
diff --git a/pp_sort.c b/pp_sort.c
index 0c5efb0869..4f81aaab7e 100644
--- a/pp_sort.c
+++ b/pp_sort.c
@@ -1067,7 +1067,8 @@ PP(pp_sort)
for (i = 0; i < max; i++)
base[i] = newSVsv(base[i]);
av_clear(av);
- av_extend(av, max);
+ if (max)
+ av_extend(av, max-1);
for (i=0; i < max; i++) {
SV * const sv = base[i];
SV ** const didstore = av_store(av, i, sv);
@@ -1094,7 +1095,7 @@ PP(pp_sort)
}
av_clear(av);
if (max > 0) {
- av_extend(av, max);
+ av_extend(av, max-1);
Copy(base, AvARRAY(av), max, SV*);
}
AvFILLp(av) = max - 1;
diff --git a/t/op/sort.t b/t/op/sort.t
index d201f00afd..f2e139dff0 100644
--- a/t/op/sort.t
+++ b/t/op/sort.t
@@ -7,7 +7,8 @@ BEGIN {
set_up_inc('../lib');
}
use warnings;
-plan(tests => 199);
+plan(tests => 203);
+use Tie::Array; # we need to test sorting tied arrays
# these shouldn't hang
{
@@ -433,7 +434,6 @@ cmp_ok($x,'eq','123',q(optimized-away comparison block doesn't take any other ar
@a = qw(b c a); $r1 = \$a[1]; @a = sort mysort @a; $r2 = \$a[0];
is "$$r1-$$r2-@a", "c-c-c b a", "inplace sort with function of lexical";
- use Tie::Array;
my @t;
tie @t, 'Tie::StdArray';
@@ -494,6 +494,25 @@ cmp_ok($x,'eq','123',q(optimized-away comparison block doesn't take any other ar
is ("@a", "3 4 5", "RT #128340");
}
+{
+ @Tied_Array_EXTEND_Test::ISA= 'Tie::StdArray';
+ my $extend_count;
+ sub Tied_Array_EXTEND_Test::EXTEND {
+ $extend_count= $_[1];
+ return;
+ }
+ my @t;
+ tie @t, "Tied_Array_EXTEND_Test";
+ is($extend_count, undef, "test that EXTEND has not been called prior to initialization");
+ $t[0]=3;
+ $t[1]=1;
+ $t[2]=2;
+ is($extend_count, undef, "test that EXTEND has not been called during initialization");
+ @t= sort @t;
+ is($extend_count, 3, "test that EXTEND was called with an argument of 3 by pp_sort()");
+ is("@t","1 2 3","test that sorting the tied array worked even though EXTEND is a no-op");
+}
+
# Test optimisations of reversed sorts. As we now guarantee stability by
# default, # optimisations which do not provide this are bogus.
--
2.21.1