Skip to content

Commit b1dca3f

Browse files
committed
add sv_numcmp() to the API
1 parent c28118d commit b1dca3f

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
@@ -8713,7 +8713,7 @@ Perl_sv_streq_flags(pTHX_ SV *sv1, SV *sv2, const U32 flags)
87138713

87148714
PERL_STATIC_INLINE bool
87158715
S_sv_numcmp_common(pTHX_ SV **sv1, SV **sv2, const U32 flags,
8716-
int method, bool *result) {
8716+
int method, SV **result) {
87178717
if(flags & SV_GMAGIC) {
87188718
if(*sv1)
87198719
SvGETMAGIC(*sv1);
@@ -8727,11 +8727,10 @@ S_sv_numcmp_common(pTHX_ SV **sv1, SV **sv2, const U32 flags,
87278727
if(!*sv2)
87288728
*sv2 = &PL_sv_undef;
87298729

8730-
SV *sv_result;
8730+
/* FIXME: do_ncmp doesn't handle "+0" overloads well */
87318731
if(!(flags & SV_SKIP_OVERLOAD) &&
87328732
(SvAMAGIC(*sv1) || SvAMAGIC(*sv2)) &&
8733-
(sv_result = amagic_call(*sv1, *sv2, method, 0))) {
8734-
*result = SvTRUE(sv_result);
8733+
(*result = amagic_call(*sv1, *sv2, method, 0))) {
87358734
return true;
87368735
}
87378736

@@ -8803,9 +8802,9 @@ Perl_sv_numeq_flags(pTHX_ SV *sv1, SV *sv2, const U32 flags)
88038802
{
88048803
PERL_ARGS_ASSERT_SV_NUMEQ_FLAGS;
88058804

8806-
bool result;
8805+
SV *result;
88078806
if (UNLIKELY(sv_numcmp_common(&sv1, &sv2, flags, eq_amg, &result)))
8808-
return result;
8807+
return SvTRUE(result);
88098808

88108809
return do_ncmp(sv1, sv2) == 0;
88118810
}
@@ -8816,13 +8815,86 @@ Perl_sv_numne_flags(pTHX_ SV *sv1, SV *sv2, const U32 flags)
88168815
PERL_ARGS_ASSERT_SV_NUMNE_FLAGS;
88178816

88188817

8819-
bool result;
8818+
SV *result;
88208819
if (UNLIKELY(sv_numcmp_common(&sv1, &sv2, flags, ne_amg, &result)))
8821-
return result;
8820+
return SvTRUE(result);
88228821

88238822
return do_ncmp(sv1, sv2) != 0;
88248823
}
88258824

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