Mailing List Archive

[internals] Safest way to iterate over a HASH
From an internal XSUB, and given an HV*, I'd like to iterate
over the whole array and deal with (key, value) pairs.

The trouble is I can't seem to be able to call hv_iternext()
like pp_each() does because there is no local iterator state
that enables me to save the previous iterator, do my own
iteration, then resume control to the previous iterator.

So if I call hv_iternext() from my XSUB, and the user calls my XSUB
from an each loop on the same HV, results will be scrambled. The
trouble is the user has no way to know I'm going to traverse that
HV in some way.

Could we add a routine to the perl internals to save the state
of any pending iteration on the HV, and then later on restore that
state. Meanwhile, the XSUB writer can do hv_iternext().

BTW, how do you know you have looped over the hash with hv_iternext?
Does it return 0?

Thanks.

Raphael
Re: [internals] Safest way to iterate over a HASH [ In reply to ]
Raphael Manfredi writes:
>
> >From an internal XSUB, and given an HV*, I'd like to iterate
> over the whole array and deal with (key, value) pairs.
>
> The trouble is I can't seem to be able to call hv_iternext()
> like pp_each() does because there is no local iterator state
> that enables me to save the previous iterator, do my own
> iteration, then resume control to the previous iterator.
>
> So if I call hv_iternext() from my XSUB, and the user calls my XSUB
> from an each loop on the same HV, results will be scrambled. The
> trouble is the user has no way to know I'm going to traverse that
> HV in some way.
>
> Could we add a routine to the perl internals to save the state
> of any pending iteration on the HV, and then later on restore that
> state. Meanwhile, the XSUB writer can do hv_iternext().
>
> BTW, how do you know you have looped over the hash with hv_iternext?
> Does it return 0?
>
> Thanks.
>
> Raphael
>

This will not help as long as this data is attached to the hash. Well,
it may help if your routine is interrupting some other iteration, but
not if somebody is interrupting your iteration.

I did it like this in Peek.xs:

case SVt_PVHV:
fprintf(stderr, " ARRAY = 0x%lx\n",(long)HvARRAY(sv));
fprintf(stderr, " KEYS = %ld\n", (long)HvKEYS(sv));
fprintf(stderr, " FILL = %ld\n", (long)HvFILL(sv));
fprintf(stderr, " MAX = %ld\n", (long)HvMAX(sv));
fprintf(stderr, " RITER = %ld\n", (long)HvRITER(sv));
fprintf(stderr, " EITER = 0x%lx\n",(long) HvEITER(sv));
if (HvPMROOT(sv))
fprintf(stderr, " PMROOT = 0x%lx\n",(long)HvPMROOT(sv));
if (HvNAME(sv))
fprintf(stderr, " NAME = \"%s\"\n", HvNAME(sv));
if (loopDump < lim && !HvEITER(sv)) { /* Try to preserve
iterator */
HE *he;
HV *hv = (HV*)sv;
int count = lim - loopDump;
I32 len;
SV *elt;
char *key;

loopDump--;
hv_iterinit(hv);
while ((elt = hv_iternextsv(hv,&key,&len)) && count--) {
fprintf(stderr, "Elt \"%s\" => 0x%lx\n", key, elt);
Dump(elt,lim);
}
hv_iterinit(hv); /* Return to status quo */
loopDump--;
}
break;



As you see, I just do nothing if EITER is present. Probably saving
RITER and EITER is enough.

Hope this helps,
Ilya