Skip to content

Commit fae0e8d

Browse files
committed
add sv_numcmp() to the API
1 parent 6e1b7ea commit fae0e8d

File tree

10 files changed

+191
-14
lines changed

10 files changed

+191
-14
lines changed

MANIFEST

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5191,6 +5191,7 @@ ext/XS-APItest/t/stuff_modify_bug.t test for eval side-effecting source string
51915191
ext/XS-APItest/t/stuff_svcur_bug.t test for a bug in lex_stuff_pvn
51925192
ext/XS-APItest/t/subcall.t Test XSUB calls
51935193
ext/XS-APItest/t/subsignature.t Test parse_subsignature()
5194+
ext/XS-APItest/t/sv_numcmp.t Test sv_numcmp
51945195
ext/XS-APItest/t/sv_numeq.t Test sv_numeq
51955196
ext/XS-APItest/t/sv_numne.t Test sv_numne
51965197
ext/XS-APItest/t/sv_streq.t Test sv_streq

embed.fnc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3414,6 +3414,11 @@ Cdp |SV * |sv_newref |NULLOK SV * const sv
34143414
Adp |void |sv_nosharing |NULLOK SV *sv
34153415
: Used in pp.c, pp_hot.c, sv.c
34163416
dpx |SV * |sv_2num |NN SV * const sv
3417+
Admp |I32 |sv_numcmp |NULLOK SV *sv1 \
3418+
|NULLOK SV *sv2
3419+
Adp |I32 |sv_numcmp_flags|NULLOK SV *sv1 \
3420+
|NULLOK SV *sv2 \
3421+
|const U32 flags
34173422
Admp |bool |sv_numeq |NULLOK SV *sv1 \
34183423
|NULLOK SV *sv2
34193424
Adp |bool |sv_numeq_flags |NULLOK SV *sv1 \
@@ -6065,7 +6070,7 @@ S |bool |sv_numcmp_common \
60656070
|NULLOK SV **sv2 \
60666071
|const U32 flags \
60676072
|int method \
6068-
|NN bool *result
6073+
|NN SV **result
60696074
S |STRLEN |sv_pos_b2u_midway \
60706075
|SPTR const U8 * const s \
60716076
|MPTR const U8 * const target \

embed.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,7 @@
726726
# define sv_newmortal() Perl_sv_newmortal(aTHX)
727727
# define sv_newref(a) Perl_sv_newref(aTHX_ a)
728728
# define sv_nosharing(a) Perl_sv_nosharing(aTHX_ a)
729+
# define sv_numcmp_flags(a,b,c) Perl_sv_numcmp_flags(aTHX_ a,b,c)
729730
# define sv_numeq_flags(a,b,c) Perl_sv_numeq_flags(aTHX_ a,b,c)
730731
# define sv_numne_flags(a,b,c) Perl_sv_numne_flags(aTHX_ a,b,c)
731732
# define sv_peek(a) Perl_sv_peek(aTHX_ a)
@@ -2346,6 +2347,7 @@
23462347
# define Perl_sv_force_normal(mTHX,a) sv_force_normal(a)
23472348
# define Perl_sv_insert(mTHX,a,b,c,d,e) sv_insert(a,b,c,d,e)
23482349
# define Perl_sv_mortalcopy(mTHX,a) sv_mortalcopy(a)
2350+
# define Perl_sv_numcmp(mTHX,a,b) sv_numcmp(a,b)
23492351
# define Perl_sv_numeq(mTHX,a,b) sv_numeq(a,b)
23502352
# define Perl_sv_numne(mTHX,a,b) sv_numne(a,b)
23512353
# define Perl_sv_pv(mTHX,a) sv_pv(a)
@@ -2448,6 +2450,7 @@
24482450
# define Perl_sv_force_normal sv_force_normal
24492451
# define Perl_sv_insert sv_insert
24502452
# define Perl_sv_mortalcopy sv_mortalcopy
2453+
# define Perl_sv_numcmp sv_numcmp
24512454
# define Perl_sv_numeq sv_numeq
24522455
# define Perl_sv_numne sv_numne
24532456
# define Perl_sv_pv sv_pv

ext/XS-APItest/APItest.xs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5038,6 +5038,12 @@ sv_numne(nullable_SV sv1, nullable_SV sv2)
50385038
bool
50395039
sv_numne_flags(nullable_SV sv1, nullable_SV sv2, U32 flags)
50405040

5041+
I32
5042+
sv_numcmp(nullable_SV sv1, nullable_SV sv2)
5043+
5044+
I32
5045+
sv_numcmp_flags(nullable_SV sv1, nullable_SV sv2, U32 flags)
5046+
50415047
bool
50425048
sv_streq(SV *sv1, SV *sv2)
50435049
CODE:

ext/XS-APItest/t/sv_numcmp.t

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!perl
2+
3+
use Test::More tests => 17;
4+
use XS::APItest;
5+
use Config;
6+
use strict;
7+
8+
my $four = 4;
9+
is sv_numcmp($four, 4), 0, '$four == 4';
10+
is sv_numcmp($four, 5), -1, '$four < 5';
11+
12+
is sv_numcmp(5, $four), 1, '5 > $four';
13+
14+
SKIP:
15+
{
16+
$Config{d_double_has_nan}
17+
or skip "No NAN", 2;
18+
my $nan = 0+"NaN";
19+
is sv_numcmp($nan, 0), 2, '$nan not comparable';
20+
is sv_numcmp($nan, $nan), 2, '$nan not comparable even with itself';
21+
}
22+
23+
my $six_point_five = 6.5; # an exact float, so == is fine
24+
is sv_numcmp($six_point_five, 6.5), 0, '$six_point_five == 6.5';
25+
is sv_numcmp($six_point_five, 6.6), -1, '$six_point_five < 6.6';
26+
27+
# NULLs
28+
is sv_numcmp(undef, 1), -1, "NULL sv1";
29+
is sv_numcmp(1, undef), 1, "NULL sv2";
30+
31+
# GMAGIC
32+
"10" =~ m/(\d+)/;
33+
is sv_numcmp_flags($1, 10, 0), -1, 'sv_numcmp_flags with no flags does not GETMAGIC';
34+
is sv_numcmp_flags($1, 10, SV_GMAGIC), 0, 'sv_numecmp_flags with SV_GMAGIC does';
35+
36+
# overloading
37+
{
38+
package AlwaysTen {
39+
use overload
40+
'<=>' => sub {
41+
return $_[2] ? $_[1] <=> 10 : 10 <=> $_[1]
42+
},
43+
'0+' => sub { 123456 };
44+
}
45+
my $obj = bless([], "AlwaysTen");
46+
47+
is sv_numcmp($obj, 10), 0, 'AlwaysTen is 10';
48+
is sv_numcmp($obj, 11), -1, 'AlwaysTen is not 11';
49+
is sv_numcmp(10, $obj), 0, 'AlwaysTen is 10 on the right';
50+
is sv_numcmp(11, $obj), 1, 'AlwaysTen is not 11 on the right';
51+
52+
SKIP:
53+
{
54+
$Config{d_double_has_nan}
55+
or skip "No NAN", 1;
56+
my $nan = 0+"NaN";
57+
58+
is sv_numcmp($obj, $nan), 2, 'AlwaysTen vs $nan is not comparable';
59+
}
60+
61+
is sv_numcmp_flags($obj, 10, SV_SKIP_OVERLOAD), 1,
62+
'AlwaysTen is not 10 with SV_SKIP_OVERLOAD';
63+
}
64+

ext/XS-APItest/t/sv_numeq.t

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
#!perl
22

3-
use Test::More tests => 13;
3+
use Test::More tests => 15;
44
use XS::APItest;
5+
use Config;
56

67
my $four = 4;
78
ok sv_numeq($four, 4), '$four == 4';
89
ok !sv_numeq($four, 5), '$four != 5';
910

11+
SKIP:
12+
{
13+
$Config{d_double_has_nan}
14+
or skip "No NAN", 2;
15+
my $nan = 0+"NaN";
16+
ok !sv_numeq($nan, 0), '$nan != 0';
17+
ok !sv_numeq($nan, $nan), '$nan != $nan';
18+
}
19+
1020
my $six_point_five = 6.5; # an exact float, so == is fine
1121
ok sv_numeq($six_point_five, 6.5), '$six_point_five == 6.5';
1222
ok !sv_numeq($six_point_five, 6.6), '$six_point_five != 6.6';

ext/XS-APItest/t/sv_numne.t

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
#!perl
22

3-
use Test::More tests => 13;
3+
use Test::More tests => 15;
44
use XS::APItest;
5+
use Config;
56

67
my $four = 4;
78
ok !sv_numne($four, 4), '$four != 4';
89
ok sv_numne($four, 5), '$four == 5';
910

11+
SKIP:
12+
{
13+
$Config{d_double_has_nan}
14+
or skip "No NAN", 2;
15+
my $nan = 0+"NaN";
16+
ok sv_numne($nan, 0), '$nan != 0';
17+
ok sv_numne($nan, $nan), '$nan != $nan';
18+
}
19+
1020
my $six_point_five = 6.5; # an exact float, so == is fine
1121
ok !sv_numne($six_point_five, 6.5), '$six_point_five == 6.5';
1222
ok sv_numne($six_point_five, 6.6), '$six_point_five != 6.6';
@@ -35,5 +45,3 @@ ok !sv_numne_flags($1, 11, SV_GMAGIC), 'sv_numne_flags with SV_GMAGIC does';
3545

3646
ok !sv_numne_flags($obj, 11, SV_SKIP_OVERLOAD), 'AlwaysTwelve is 12 with SV_SKIP_OVERLOAD'
3747
}
38-
39-
done_testing();

proto.h

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sv.c

Lines changed: 80 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8707,7 +8707,7 @@ Perl_sv_streq_flags(pTHX_ SV *sv1, SV *sv2, const U32 flags)
87078707

87088708
PERL_STATIC_INLINE bool
87098709
S_sv_numcmp_common(pTHX_ SV **sv1, SV **sv2, const U32 flags,
8710-
int method, bool *result) {
8710+
int method, SV **result) {
87118711
if(flags & SV_GMAGIC) {
87128712
if(*sv1)
87138713
SvGETMAGIC(*sv1);
@@ -8721,11 +8721,10 @@ S_sv_numcmp_common(pTHX_ SV **sv1, SV **sv2, const U32 flags,
87218721
if(!*sv2)
87228722
*sv2 = &PL_sv_undef;
87238723

8724-
SV *sv_result;
8724+
/* FIXME: do_ncmp doesn't handle "+0" overloads well */
87258725
if(!(flags & SV_SKIP_OVERLOAD) &&
87268726
(SvAMAGIC(*sv1) || SvAMAGIC(*sv2)) &&
8727-
(sv_result = amagic_call(*sv1, *sv2, method, 0))) {
8728-
*result = SvTRUE(sv_result);
8727+
(*result = amagic_call(*sv1, *sv2, method, 0))) {
87298728
return true;
87308729
}
87318730

@@ -8797,9 +8796,9 @@ Perl_sv_numeq_flags(pTHX_ SV *sv1, SV *sv2, const U32 flags)
87978796
{
87988797
PERL_ARGS_ASSERT_SV_NUMEQ_FLAGS;
87998798

8800-
bool result;
8799+
SV *result;
88018800
if (UNLIKELY(sv_numcmp_common(&sv1, &sv2, flags, eq_amg, &result)))
8802-
return result;
8801+
return SvTRUE(result);
88038802

88048803
return do_ncmp(sv1, sv2) == 0;
88058804
}
@@ -8810,13 +8809,86 @@ Perl_sv_numne_flags(pTHX_ SV *sv1, SV *sv2, const U32 flags)
88108809
PERL_ARGS_ASSERT_SV_NUMNE_FLAGS;
88118810

88128811

8813-
bool result;
8812+
SV *result;
88148813
if (UNLIKELY(sv_numcmp_common(&sv1, &sv2, flags, ne_amg, &result)))
8815-
return result;
8814+
return SvTRUE(result);
88168815

88178816
return do_ncmp(sv1, sv2) != 0;
88188817
}
88198818

8819+
/*
8820+
=for apidoc sv_numcmp
8821+
=for apidoc_item sv_numcmp_flags
8822+
8823+
This returns an integer indicating the ordering of the two SV
8824+
arguments, coercing them to numbers if necessary, basically behaving
8825+
like the Perl code S<C<$sv1 <=> $sv2 >>.
8826+
8827+
A NULL SV is treated as C<undef>.
8828+
8829+
This will return one of the following values:
8830+
8831+
=over
8832+
8833+
=item *
8834+
8835+
C<1> - C<sv2> is numerically greater than C<sv1>
8836+
8837+
=item *
8838+
8839+
C<0> - C<sv1> and C<sv2> are numerically equal.
8840+
8841+
=item *
8842+
8843+
C<-1> - C<sv2> is numerically less than C<sv1>
8844+
8845+
=item *
8846+
8847+
C<2> - C<sv1> and C<sv2> are not numerically comparable, probably
8848+
because one of them is C<NaN>, though overloads can extend that.
8849+
8850+
=back
8851+
8852+
C<sv_numcmp> always performs 'get' magic. C<sv_numcmp_flags> performs
8853+
'get' magic on if C<flags> has the C<SV_GMAGIC> bit set.
8854+
8855+
C<sv_numcmp> always checks for, and if present, handles C<< <=> >>
8856+
overloading. If not present, regular numerical comparison will be
8857+
used instead.
8858+
C<sv_numcmp_flags> normally does the same, but if the
8859+
C<SV_SKIP_OVERLOAD> bit is set in C<flags> any C<< <=> >> overloading
8860+
is ignored and a regular numerical comparison is done instead.
8861+
8862+
=cut
8863+
*/
8864+
8865+
#define SANE_ORDERING_RESULT(val) \
8866+
((val) < 0 ? -1 : (val) > 0 ? 1 : 0)
8867+
8868+
I32
8869+
Perl_sv_numcmp_flags(pTHX_ SV *sv1, SV *sv2, const U32 flags)
8870+
{
8871+
PERL_ARGS_ASSERT_SV_NUMCMP_FLAGS;
8872+
8873+
SV *result;
8874+
if (UNLIKELY(sv_numcmp_common(&sv1, &sv2, flags, ncmp_amg, &result))) {
8875+
/* Similar to what sort() does in amagic_ncmp() */
8876+
if (SvIOK(result) && !SvIsUV(result)) {
8877+
IV i = SvIVX(result);
8878+
return SANE_ORDERING_RESULT(i);
8879+
}
8880+
else if (!SvOK(result)) {
8881+
return 2;
8882+
}
8883+
else {
8884+
NV nv = SvNV(result);
8885+
return SANE_ORDERING_RESULT(nv);
8886+
}
8887+
}
8888+
8889+
return do_ncmp(sv1, sv2);
8890+
}
8891+
88208892
/*
88218893
=for apidoc sv_cmp
88228894
=for apidoc_item sv_cmp_flags

sv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2323,6 +2323,7 @@ Usually accessed via the C<SvPVutf8_nolen> macro.
23232323
#define sv_cmp_locale(sv1, sv2) sv_cmp_locale_flags(sv1, sv2, SV_GMAGIC)
23242324
#define sv_numeq(sv1, sv2) sv_numeq_flags(sv1, sv2, SV_GMAGIC)
23252325
#define sv_numne(sv1, sv2) sv_numne_flags(sv1, sv2, SV_GMAGIC)
2326+
#define sv_numcmp(sv1, sv2) sv_numcmp_flags(sv1, sv2, SV_GMAGIC)
23262327
#define sv_streq(sv1, sv2) sv_streq_flags(sv1, sv2, SV_GMAGIC)
23272328
#define sv_collxfrm(sv, nxp) sv_collxfrm_flags(sv, nxp, SV_GMAGIC)
23282329
#define sv_2bool(sv) sv_2bool_flags(sv, SV_GMAGIC)

0 commit comments

Comments
 (0)