summaryrefslogtreecommitdiff
path: root/hv.c
diff options
context:
space:
mode:
authorDavid Mitchell <davem@iabyn.com>2017-07-21 14:32:57 +0100
committerDavid Mitchell <davem@iabyn.com>2017-07-27 11:30:24 +0100
commit8dc9003ff3914e78971f561eaece965e9ceeb49e (patch)
tree9ddb6c6946453365682c422f311a985fce2725ef /hv.c
parenta16ac36c3b97d0d80762b2900667f3759f354d9f (diff)
downloadperl-8dc9003ff3914e78971f561eaece965e9ceeb49e.tar.gz
hv_pushkv(): handle keys() and values() too
The newish function hv_pushkv() currently just pushes all key/value pairs on the stack. i.e. it does the equivalent of the perl code '() = %h'. Extend it so that it can handle 'keys %h' and values %h' too. This is basically moving the remaining list-context functionality out of do_kv() and into hv_pushkv(). The rationale for this is that hv_pushkv() is a pure HV-related function, while do_kv() is a pp function for several ops including OP_KEYS/VALUES, and expects PL_op->op_flags/op_private to be valid.
Diffstat (limited to 'hv.c')
-rw-r--r--hv.c51
1 files changed, 35 insertions, 16 deletions
diff --git a/hv.c b/hv.c
index 96228b398c..20b4eceb98 100644
--- a/hv.c
+++ b/hv.c
@@ -969,47 +969,66 @@ Perl_hv_scalar(pTHX_ HV *hv)
/*
-Pushes all the keys and values of a hash onto the stack.
+hv_pushkv(): push all the keys and/or values of a hash onto the stack.
+The rough Perl equivalents:
+ () = %hash;
+ () = keys %hash;
+ () = values %hash;
+
Resets the hash's iterator.
-The rough Perl equivalent: C< () = %hash; >
-XXX this may at some point be extended to push 'keys %h' and 'values %h'
-too. I might also unroll hv_iternext() - DAPM
+
+flags : 1 = push keys
+ 2 = push values
+ 1|2 = push keys and values
+ XXX use symbolic flag constants at some point?
+I might unroll the non-tied hv_iternext() in here at some point - DAPM
*/
void
-Perl_hv_pushkv(pTHX_ HV *hv)
+Perl_hv_pushkv(pTHX_ HV *hv, U32 flags)
{
HE *entry;
bool tied = SvRMAGICAL(hv) && mg_find(MUTABLE_SV(hv), PERL_MAGIC_tied);
dSP;
PERL_ARGS_ASSERT_HV_PUSHKV;
+ assert(flags); /* must be pushing at least one of keys and values */
(void)hv_iterinit(hv);
if (tied) {
+ SSize_t ext = (flags == 3) ? 2 : 1;
while ((entry = hv_iternext(hv))) {
- EXTEND(SP, 2);
- PUSHs(hv_iterkeysv(entry));
- PUSHs(hv_iterval(hv, entry));
+ EXTEND(SP, ext);
+ if (flags & 1)
+ PUSHs(hv_iterkeysv(entry));
+ if (flags & 2)
+ PUSHs(hv_iterval(hv, entry));
}
}
else {
Size_t nkeys = HvUSEDKEYS(hv);
- SSize_t nkv;
+ SSize_t ext;
+
+ if (!nkeys)
+ return;
+
/* 2*nkeys() should never be big enough to truncate or wrap */
assert(nkeys <= (SSize_t_MAX >> 1));
- nkv = nkeys * 2;
+ ext = nkeys * ((flags == 3) ? 2 : 1);
EXTEND_MORTAL(nkeys);
- EXTEND(SP, nkv);
+ EXTEND(SP, ext);
while ((entry = hv_iternext(hv))) {
- SV *keysv = newSVhek(HeKEY_hek(entry));
- SvTEMP_on(keysv);
- PL_tmps_stack[++PL_tmps_ix] = keysv;
- PUSHs(keysv);
- PUSHs(HeVAL(entry));
+ if (flags & 1) {
+ SV *keysv = newSVhek(HeKEY_hek(entry));
+ SvTEMP_on(keysv);
+ PL_tmps_stack[++PL_tmps_ix] = keysv;
+ PUSHs(keysv);
+ }
+ if (flags & 2)
+ PUSHs(HeVAL(entry));
}
}