/* pp_sort.c
*
- * Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- * 2000, 2001, 2002, 2003, 2004, 2005, 2006, by Larry Wall and others
+ * Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+ * 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 by Larry Wall and others
*
* You may distribute under the terms of either the GNU General Public
* License or the Artistic License, as specified in the README file.
*/
/*
- * ...they shuffled back towards the rear of the line. 'No, not at the
- * rear!' the slave-driver shouted. 'Three files up. And stay there...
+ * ...they shuffled back towards the rear of the line. 'No, not at the
+ * rear!' the slave-driver shouted. 'Three files up. And stay there...
+ *
+ * [p.931 of _The Lord of the Rings_, VI/ii: "The Land of Shadow"]
*/
/* This file contains pp ("push/pop") functions that
#define FROMTOUPTO(src, dst, lim) do *dst++ = *src++; while(src<lim)
-/* Runs are identified by a pointer in the auxilliary list.
+/* Runs are identified by a pointer in the auxiliary list.
** The pointer is at the start of the list,
** and it points to the start of the next list.
** NEXT is used as an lvalue, too.
if (r >= t) p = r = t; /* too short to care about */
else {
while (((cmp(aTHX_ *(p-1), *p) > 0) == sense) &&
- ((p -= 2) > q));
+ ((p -= 2) > q)) {}
if (p <= q) {
/* b through r is a (long) run.
** Extend it as far as possible.
static I32
-cmp_desc(pTHX_ gptr a, gptr b)
+cmp_desc(pTHX_ gptr const a, gptr const b)
{
dVAR;
return -PL_sort_RealCmp(aTHX_ a, b);
}
if (nmemb <= SMALLSORT) aux = small; /* use stack for aux array */
- else { Newx(aux,nmemb,gptr); } /* allocate auxilliary array */
+ else { Newx(aux,nmemb,gptr); } /* allocate auxiliary array */
level = 0;
stackp = stack;
stackp->runs = dynprep(aTHX_ base, aux, nmemb, cmp);
t = NEXT(t); /* where second run will end */
t = PINDEX(base, PNELEM(aux, t)); /* where it now ends */
FROMTOUPTO(f1, f2, t); /* copy both runs */
- NEXT(b) = p; /* paralled pointer for 1st */
+ NEXT(b) = p; /* paralleled pointer for 1st */
NEXT(p) = t; /* ... and for second */
}
}
S_qsortsvu(pTHX_ SV ** array, size_t num_elts, SVCOMPARE_t compare)
{
register SV * temp;
-
struct partition_stack_entry partition_stack[QSORT_MAX_STACK];
int next_stack_entry = 0;
-
int part_left;
int part_right;
#ifdef QSORT_ORDER_GUESS
int swapped;
#endif
+ PERL_ARGS_ASSERT_QSORTSVU;
+
/* Make sure we actually have work to do.
*/
if (num_elts <= 1) {
return;
}
- /* Innoculate large partitions against quadratic behavior */
+ /* Inoculate large partitions against quadratic behavior */
if (num_elts > QSORT_PLAY_SAFE) {
register size_t n;
register SV ** const q = array;
* by the original comparison routine on the elements pointed to.
* Because we don't move the elements of list1 around through
* this phase, we can break ties on elements that compare equal
- * using their address in the list1 array, ensuring stabilty.
+ * using their address in the list1 array, ensuring stability.
* This leaves us with something looking like
*
* indir list1
static I32
-cmpindir(pTHX_ gptr a, gptr b)
+cmpindir(pTHX_ gptr const a, gptr const b)
{
dVAR;
gptr * const ap = (gptr *)a;
}
static I32
-cmpindir_desc(pTHX_ gptr a, gptr b)
+cmpindir_desc(pTHX_ gptr const a, gptr const b)
{
dVAR;
gptr * const ap = (gptr *)a;
q = list1;
for (n = nmemb; n--; ) {
/* Assert A: all elements of q with index > n are already
- * in place. This is vacuosly true at the start, and we
+ * in place. This is vacuously true at the start, and we
* put element n where it belongs below (if it wasn't
* already where it belonged). Assert B: we only move
* elements that aren't where they belong,
void
Perl_sortsv(pTHX_ SV **array, size_t nmemb, SVCOMPARE_t cmp)
{
+ PERL_ARGS_ASSERT_SORTSV;
+
sortsv_flags(array, nmemb, cmp, 0);
}
void
Perl_sortsv_flags(pTHX_ SV **array, size_t nmemb, SVCOMPARE_t cmp, U32 flags)
{
+ PERL_ARGS_ASSERT_SORTSV_FLAGS;
+
if (flags & SORTf_QSORT)
S_qsortsv(aTHX_ array, nmemb, cmp, flags);
else
stash = CopSTASH(PL_curcop);
}
else {
- cv = sv_2cv(*++MARK, &stash, &gv, 0);
+ GV *autogv = NULL;
+ cv = sv_2cv(*++MARK, &stash, &gv, GV_ADD);
+ check_cv:
if (cv && SvPOK(cv)) {
- const char * const proto = SvPV_nolen_const((SV*)cv);
+ const char * const proto = SvPV_nolen_const(MUTABLE_SV(cv));
if (proto && strEQ(proto, "$$")) {
hasargs = TRUE;
}
}
- if (!(cv && CvROOT(cv))) {
- if (cv && CvISXSUB(cv)) {
- is_xsub = 1;
+ if (cv && CvISXSUB(cv) && CvXSUB(cv)) {
+ is_xsub = 1;
+ }
+ else if (!(cv && CvROOT(cv))) {
+ if (gv) {
+ goto autoload;
}
- else if (gv) {
+ else if (!CvANON(cv) && (gv = CvGV(cv))) {
+ if (cv != GvCV(gv)) cv = GvCV(gv);
+ autoload:
+ if (!autogv && (
+ autogv = gv_autoload_pvn(
+ GvSTASH(gv), GvNAME(gv), GvNAMELEN(gv),
+ GvNAMEUTF8(gv) ? SVf_UTF8 : 0
+ )
+ )) {
+ cv = GvCVu(autogv);
+ goto check_cv;
+ }
+ else {
SV *tmpstr = sv_newmortal();
gv_efullname3(tmpstr, gv, NULL);
DIE(aTHX_ "Undefined sort subroutine \"%"SVf"\" called",
- (void*)tmpstr);
+ SVfARG(tmpstr));
+ }
}
else {
DIE(aTHX_ "Undefined subroutine in sort");
if (priv & OPpSORT_INPLACE) {
assert( MARK+1 == SP && *SP && SvTYPE(*SP) == SVt_PVAV);
(void)POPMARK; /* remove mark associated with ex-OP_AASSIGN */
- av = (AV*)(*SP);
+ av = MUTABLE_AV((*SP));
max = AvFILL(av) + 1;
if (SvMAGICAL(av)) {
MEXTEND(SP, max);
- p2 = SP;
for (i=0; i < max; i++) {
SV **svp = av_fetch(av, i, FALSE);
*SP++ = (svp) ? *svp : NULL;
}
+ SP--;
+ p1 = p2 = SP - (max-1);
}
else {
if (SvREADONLY(av))
- Perl_croak(aTHX_ PL_no_modify);
+ Perl_croak_no_modify(aTHX);
else
SvREADONLY_on(av);
p1 = p2 = AvARRAY(av);
if (!PL_sortcop) {
if (priv & OPpSORT_NUMERIC) {
if (priv & OPpSORT_INTEGER) {
- if (!SvIOK(*p1)) {
- if (SvAMAGIC(*p1))
- overloading = 1;
- else
- (void)sv_2iv(*p1);
- }
+ if (!SvIOK(*p1))
+ (void)sv_2iv_flags(*p1, SV_GMAGIC|SV_SKIP_OVERLOAD);
}
else {
- if (!SvNSIOK(*p1)) {
- if (SvAMAGIC(*p1))
- overloading = 1;
- else
- (void)sv_2nv(*p1);
- }
+ if (!SvNSIOK(*p1))
+ (void)sv_2nv_flags(*p1, SV_GMAGIC|SV_SKIP_OVERLOAD);
if (all_SIVs && !SvSIOK(*p1))
all_SIVs = 0;
}
}
else {
- if (!SvPOK(*p1)) {
- if (SvAMAGIC(*p1))
- overloading = 1;
- else
- (void)sv_2pv_flags(*p1, 0,
- SV_GMAGIC|SV_CONST_RETURN);
- }
+ if (!SvPOK(*p1))
+ (void)sv_2pv_flags(*p1, 0,
+ SV_GMAGIC|SV_CONST_RETURN|SV_SKIP_OVERLOAD);
}
+ if (SvAMAGIC(*p1))
+ overloading = 1;
}
p1++;
}
if (!(flags & OPf_SPECIAL)) {
cx->cx_type = CXt_SUB;
cx->blk_gimme = G_SCALAR;
+ /* If our comparison routine is already active (CvDEPTH is
+ * is not 0), then PUSHSUB does not increase the refcount,
+ * so we have to do it ourselves, because the LEAVESUB fur-
+ * ther down lowers it. */
+ if (CvDEPTH(cv)) SvREFCNT_inc_simple_void_NN(cv);
PUSHSUB(cx);
if (!is_xsub) {
AV* const padlist = CvPADLIST(cv);
if (hasargs) {
/* This is mostly copied from pp_entersub */
- AV * const av = (AV*)PAD_SVl(0);
+ AV * const av = MUTABLE_AV(PAD_SVl(0));
cx->blk_sub.savearray = GvAV(PL_defgv);
- GvAV(PL_defgv) = (AV*)SvREFCNT_inc_simple(av);
+ GvAV(PL_defgv) = MUTABLE_AV(SvREFCNT_inc_simple(av));
CX_CURPAD_SAVE(cx->blk_sub);
cx->blk_sub.argarray = av;
}
sort_flags);
if (!(flags & OPf_SPECIAL)) {
- LEAVESUB(cv);
- if (!is_xsub)
- CvDEPTH(cv)--;
+ SV *sv;
+ /* Reset cx, in case the context stack has been
+ reallocated. */
+ cx = &cxstack[cxstack_ix];
+ POPSUB(cx, sv);
+ LEAVESUB(sv);
}
POPBLOCK(cx,PL_curpm);
PL_stack_sp = newsp;
: ( overloading ? S_amagic_ncmp : S_sv_ncmp ) )
: ( IN_LOCALE_RUNTIME
? ( overloading
- ? S_amagic_cmp_locale
- : sv_cmp_locale_static)
- : ( overloading ? S_amagic_cmp : sv_cmp_static)),
+ ? (SVCOMPARE_t)S_amagic_cmp_locale
+ : (SVCOMPARE_t)sv_cmp_locale_static)
+ : ( overloading ? (SVCOMPARE_t)S_amagic_cmp : (SVCOMPARE_t)sv_cmp_static)),
sort_flags);
}
if ((priv & OPpSORT_REVERSE) != 0) {
SvREADONLY_off(av);
else if (av && !sorting_av) {
/* simulate pp_aassign of tied AV */
- SV** const base = ORIGMARK+1;
+ SV** const base = MARK+1;
for (i=0; i < max; i++) {
base[i] = newSVsv(base[i]);
}
}
static I32
-S_sortcv(pTHX_ SV *a, SV *b)
+S_sortcv(pTHX_ SV *const a, SV *const b)
{
dVAR;
const I32 oldsaveix = PL_savestack_ix;
const I32 oldscopeix = PL_scopestack_ix;
I32 result;
+ PMOP * const pm = PL_curpm;
+ OP * const sortop = PL_op;
+ COP * const cop = PL_curcop;
+ SV **pad;
+
+ PERL_ARGS_ASSERT_SORTCV;
+
GvSV(PL_firstgv) = a;
GvSV(PL_secondgv) = b;
PL_stack_sp = PL_stack_base;
PL_op = PL_sortcop;
CALLRUNOPS(aTHX);
- if (PL_stack_sp != PL_stack_base + 1)
- Perl_croak(aTHX_ "Sort subroutine didn't return single value");
- if (!SvNIOKp(*PL_stack_sp))
- Perl_croak(aTHX_ "Sort subroutine didn't return a numeric value");
- result = SvIV(*PL_stack_sp);
+ PL_op = sortop;
+ PL_curcop = cop;
+ pad = PL_curpad; PL_curpad = 0;
+ if (PL_stack_sp != PL_stack_base + 1) {
+ assert(PL_stack_sp == PL_stack_base);
+ result = SvIV(&PL_sv_undef);
+ }
+ else result = SvIV(*PL_stack_sp);
+ PL_curpad = pad;
while (PL_scopestack_ix > oldscopeix) {
LEAVE;
}
leave_scope(oldsaveix);
+ PL_curpm = pm;
return result;
}
static I32
-S_sortcv_stacked(pTHX_ SV *a, SV *b)
+S_sortcv_stacked(pTHX_ SV *const a, SV *const b)
{
dVAR;
const I32 oldsaveix = PL_savestack_ix;
const I32 oldscopeix = PL_scopestack_ix;
I32 result;
AV * const av = GvAV(PL_defgv);
+ PMOP * const pm = PL_curpm;
+ OP * const sortop = PL_op;
+ COP * const cop = PL_curcop;
+ SV **pad;
+ PERL_ARGS_ASSERT_SORTCV_STACKED;
+
+ if (AvREAL(av)) {
+ av_clear(av);
+ AvREAL_off(av);
+ AvREIFY_on(av);
+ }
if (AvMAX(av) < 1) {
- SV** ary = AvALLOC(av);
+ SV **ary = AvALLOC(av);
if (AvARRAY(av) != ary) {
AvMAX(av) += AvARRAY(av) - AvALLOC(av);
- SvPV_set(av, (char*)ary);
+ AvARRAY(av) = ary;
}
if (AvMAX(av) < 1) {
AvMAX(av) = 1;
Renew(ary,2,SV*);
- SvPV_set(av, (char*)ary);
+ AvARRAY(av) = ary;
+ AvALLOC(av) = ary;
}
}
AvFILLp(av) = 1;
PL_stack_sp = PL_stack_base;
PL_op = PL_sortcop;
CALLRUNOPS(aTHX);
- if (PL_stack_sp != PL_stack_base + 1)
- Perl_croak(aTHX_ "Sort subroutine didn't return single value");
- if (!SvNIOKp(*PL_stack_sp))
- Perl_croak(aTHX_ "Sort subroutine didn't return a numeric value");
- result = SvIV(*PL_stack_sp);
+ PL_op = sortop;
+ PL_curcop = cop;
+ pad = PL_curpad; PL_curpad = 0;
+ if (PL_stack_sp != PL_stack_base + 1) {
+ assert(PL_stack_sp == PL_stack_base);
+ result = SvIV(&PL_sv_undef);
+ }
+ else result = SvIV(*PL_stack_sp);
+ PL_curpad = pad;
while (PL_scopestack_ix > oldscopeix) {
LEAVE;
}
leave_scope(oldsaveix);
+ PL_curpm = pm;
return result;
}
static I32
-S_sortcv_xsub(pTHX_ SV *a, SV *b)
+S_sortcv_xsub(pTHX_ SV *const a, SV *const b)
{
dVAR; dSP;
const I32 oldsaveix = PL_savestack_ix;
const I32 oldscopeix = PL_scopestack_ix;
- CV * const cv=(CV*)PL_sortcop;
+ CV * const cv=MUTABLE_CV(PL_sortcop);
I32 result;
+ PMOP * const pm = PL_curpm;
+
+ PERL_ARGS_ASSERT_SORTCV_XSUB;
SP = PL_stack_base;
PUSHMARK(SP);
(void)(*CvXSUB(cv))(aTHX_ cv);
if (PL_stack_sp != PL_stack_base + 1)
Perl_croak(aTHX_ "Sort subroutine didn't return single value");
- if (!SvNIOKp(*PL_stack_sp))
- Perl_croak(aTHX_ "Sort subroutine didn't return a numeric value");
result = SvIV(*PL_stack_sp);
while (PL_scopestack_ix > oldscopeix) {
LEAVE;
}
leave_scope(oldsaveix);
+ PL_curpm = pm;
return result;
}
static I32
-S_sv_ncmp(pTHX_ SV *a, SV *b)
+S_sv_ncmp(pTHX_ SV *const a, SV *const b)
{
const NV nv1 = SvNSIV(a);
const NV nv2 = SvNSIV(b);
+
+ PERL_ARGS_ASSERT_SV_NCMP;
+
+#if defined(NAN_COMPARE_BROKEN) && defined(Perl_isnan)
+ if (Perl_isnan(nv1) || Perl_isnan(nv2)) {
+#else
+ if (nv1 != nv1 || nv2 != nv2) {
+#endif
+ if (ckWARN(WARN_UNINITIALIZED)) report_uninit(NULL);
+ return 0;
+ }
return nv1 < nv2 ? -1 : nv1 > nv2 ? 1 : 0;
}
static I32
-S_sv_i_ncmp(pTHX_ SV *a, SV *b)
+S_sv_i_ncmp(pTHX_ SV *const a, SV *const b)
{
const IV iv1 = SvIV(a);
const IV iv2 = SvIV(b);
+
+ PERL_ARGS_ASSERT_SV_I_NCMP;
+
return iv1 < iv2 ? -1 : iv1 > iv2 ? 1 : 0;
}
#define tryCALL_AMAGICbin(left,right,meth) \
- (PL_amagic_generation && (SvAMAGIC(left)||SvAMAGIC(right))) \
- ? amagic_call(left, right, CAT2(meth,_amg), 0) \
+ (SvAMAGIC(left)||SvAMAGIC(right)) \
+ ? amagic_call(left, right, meth, 0) \
: NULL;
+#define SORT_NORMAL_RETURN_VALUE(val) (((val) > 0) ? 1 : ((val) ? -1 : 0))
+
static I32
-S_amagic_ncmp(pTHX_ register SV *a, register SV *b)
+S_amagic_ncmp(pTHX_ register SV *const a, register SV *const b)
{
dVAR;
- SV * const tmpsv = tryCALL_AMAGICbin(a,b,ncmp);
+ SV * const tmpsv = tryCALL_AMAGICbin(a,b,ncmp_amg);
+
+ PERL_ARGS_ASSERT_AMAGIC_NCMP;
+
if (tmpsv) {
if (SvIOK(tmpsv)) {
const I32 i = SvIVX(tmpsv);
- if (i > 0)
- return 1;
- return i? -1 : 0;
+ return SORT_NORMAL_RETURN_VALUE(i);
}
else {
const NV d = SvNV(tmpsv);
- if (d > 0)
- return 1;
- return d ? -1 : 0;
+ return SORT_NORMAL_RETURN_VALUE(d);
}
}
return S_sv_ncmp(aTHX_ a, b);
}
static I32
-S_amagic_i_ncmp(pTHX_ register SV *a, register SV *b)
+S_amagic_i_ncmp(pTHX_ register SV *const a, register SV *const b)
{
dVAR;
- SV * const tmpsv = tryCALL_AMAGICbin(a,b,ncmp);
+ SV * const tmpsv = tryCALL_AMAGICbin(a,b,ncmp_amg);
+
+ PERL_ARGS_ASSERT_AMAGIC_I_NCMP;
+
if (tmpsv) {
if (SvIOK(tmpsv)) {
const I32 i = SvIVX(tmpsv);
- if (i > 0)
- return 1;
- return i? -1 : 0;
+ return SORT_NORMAL_RETURN_VALUE(i);
}
else {
const NV d = SvNV(tmpsv);
- if (d > 0)
- return 1;
- return d ? -1 : 0;
+ return SORT_NORMAL_RETURN_VALUE(d);
}
}
return S_sv_i_ncmp(aTHX_ a, b);
}
static I32
-S_amagic_cmp(pTHX_ register SV *str1, register SV *str2)
+S_amagic_cmp(pTHX_ register SV *const str1, register SV *const str2)
{
dVAR;
- SV * const tmpsv = tryCALL_AMAGICbin(str1,str2,scmp);
+ SV * const tmpsv = tryCALL_AMAGICbin(str1,str2,scmp_amg);
+
+ PERL_ARGS_ASSERT_AMAGIC_CMP;
+
if (tmpsv) {
if (SvIOK(tmpsv)) {
const I32 i = SvIVX(tmpsv);
- if (i > 0)
- return 1;
- return i? -1 : 0;
+ return SORT_NORMAL_RETURN_VALUE(i);
}
else {
const NV d = SvNV(tmpsv);
- if (d > 0)
- return 1;
- return d? -1 : 0;
+ return SORT_NORMAL_RETURN_VALUE(d);
}
}
return sv_cmp(str1, str2);
}
static I32
-S_amagic_cmp_locale(pTHX_ register SV *str1, register SV *str2)
+S_amagic_cmp_locale(pTHX_ register SV *const str1, register SV *const str2)
{
dVAR;
- SV * const tmpsv = tryCALL_AMAGICbin(str1,str2,scmp);
+ SV * const tmpsv = tryCALL_AMAGICbin(str1,str2,scmp_amg);
+
+ PERL_ARGS_ASSERT_AMAGIC_CMP_LOCALE;
+
if (tmpsv) {
if (SvIOK(tmpsv)) {
const I32 i = SvIVX(tmpsv);
- if (i > 0)
- return 1;
- return i? -1 : 0;
+ return SORT_NORMAL_RETURN_VALUE(i);
}
else {
const NV d = SvNV(tmpsv);
- if (d > 0)
- return 1;
- return d? -1 : 0;
+ return SORT_NORMAL_RETURN_VALUE(d);
}
}
return sv_cmp_locale(str1, str2);