#include "EXTERN.h"
#define PERL_IN_HV_C
#define PERL_HASH_INTERNAL_ACCESS
#include "perl.h"
#define MAX_BUCKET_MAX ((1<<26)-1)
#define DO_HSPLIT(xhv) ( ( ((xhv)->xhv_keys + ((xhv)->xhv_keys >> 1)) > (xhv)->xhv_max ) && \
((xhv)->xhv_max < MAX_BUCKET_MAX) )
static
const
char
S_strtab_error[]
=
"Cannot modify shared string table in hv_%s"
;
#define DEBUG_HASH_RAND_BITS (DEBUG_h_TEST)
#if IVSIZE == 8
#define XORSHIFT_RAND_BITS(x) PERL_XORSHIFT64_A(x)
#else
#define XORSHIFT_RAND_BITS(x) PERL_XORSHIFT32_A(x)
#endif
#define UPDATE_HASH_RAND_BITS_KEY(key,klen) \
STMT_START { \
XORSHIFT_RAND_BITS(PL_hash_rand_bits); \
if
(DEBUG_HASH_RAND_BITS) { \
PerlIO_printf( Perl_debug_log, \
"PL_hash_rand_bits=%016"
UVxf
" @ %s:%-4d"
, \
(UV)PL_hash_rand_bits, __FILE__, __LINE__ \
); \
if
(DEBUG_v_TEST && key) { \
PerlIO_printf( Perl_debug_log,
" key:'%.*s' %"
UVuf
"\n"
, \
(
int
)klen, \
key ? key :
""
,
\
(UV)klen \
); \
}
else
{ \
PerlIO_printf( Perl_debug_log,
"\n"
); \
} \
} \
} STMT_END
#define MAYBE_UPDATE_HASH_RAND_BITS_KEY(key,klen) \
STMT_START { \
if
(PL_HASH_RAND_BITS_ENABLED) \
UPDATE_HASH_RAND_BITS_KEY(key,klen); \
} STMT_END
#define UPDATE_HASH_RAND_BITS() \
UPDATE_HASH_RAND_BITS_KEY(NULL,0)
#define MAYBE_UPDATE_HASH_RAND_BITS() \
MAYBE_UPDATE_HASH_RAND_BITS_KEY(NULL,0)
#define HVhek_STORAGE_MASK (0xFF & ~HVhek_NOTSHARED)
#ifdef PURIFY
#define new_HE() (HE*)safemalloc(sizeof(HE))
#define del_HE(p) safefree((char*)p)
#else
STATIC HE*
S_new_he(pTHX)
{
HE* he;
void
**
const
root = &PL_body_roots[HE_ARENA_ROOT_IX];
if
(!*root)
Perl_more_bodies(aTHX_ HE_ARENA_ROOT_IX,
sizeof
(HE), PERL_ARENA_SIZE);
he = (HE*) *root;
assert
(he);
*root = HeNEXT(he);
return
he;
}
#define new_HE() new_he()
#define del_HE(p) \
STMT_START { \
HeNEXT(p) = (HE*)(PL_body_roots[HE_ARENA_ROOT_IX]); \
PL_body_roots[HE_ARENA_ROOT_IX] = p; \
} STMT_END
#endif
STATIC HEK *
S_save_hek_flags(
const
char
*str, I32 len, U32 hash,
int
flags)
{
char
*k;
HEK *hek;
PERL_ARGS_ASSERT_SAVE_HEK_FLAGS;
Newx(k, HEK_BASESIZE + len + 2,
char
);
hek = (HEK*)k;
Copy(str, HEK_KEY(hek), len,
char
);
HEK_KEY(hek)[len] = 0;
HEK_LEN(hek) = len;
HEK_HASH(hek) = hash;
HEK_FLAGS(hek) = HVhek_NOTSHARED | (flags & HVhek_STORAGE_MASK);
if
(flags & HVhek_FREEKEY)
Safefree(str);
return
hek;
}
void
Perl_free_tied_hv_pool(pTHX)
{
HE *he = PL_hv_fetch_ent_mh;
while
(he) {
HE *
const
ohe = he;
Safefree(HeKEY_hek(he));
he = HeNEXT(he);
del_HE(ohe);
}
PL_hv_fetch_ent_mh = NULL;
}
#if defined(USE_ITHREADS)
HEK *
Perl_hek_dup(pTHX_ HEK *source, CLONE_PARAMS* param)
{
HEK *shared;
PERL_ARGS_ASSERT_HEK_DUP;
PERL_UNUSED_ARG(param);
if
(!source)
return
NULL;
shared = (HEK*)ptr_table_fetch(PL_ptr_table, source);
if
(shared) {
(
void
)share_hek_hek(shared);
}
else
{
shared
= share_hek_flags(HEK_KEY(source), HEK_LEN(source),
HEK_HASH(source), HEK_FLAGS(source));
ptr_table_store(PL_ptr_table, source, shared);
}
return
shared;
}
HE *
Perl_he_dup(pTHX_
const
HE *e,
bool
shared, CLONE_PARAMS* param)
{
HE *ret;
PERL_ARGS_ASSERT_HE_DUP;
PERL_UNUSED_ARG(shared);
if
(!e)
return
NULL;
ret = (HE*)ptr_table_fetch(PL_ptr_table, e);
if
(ret)
return
ret;
ret = new_HE();
ptr_table_store(PL_ptr_table, e, ret);
if
(HeKLEN(e) == HEf_SVKEY) {
char
*k;
Newx(k, HEK_BASESIZE +
sizeof
(
const
SV *),
char
);
HeKEY_hek(ret) = (HEK*)k;
HeKEY_sv(ret) = sv_dup_inc(HeKEY_sv(e), param);
}
else
if
(!(HeKFLAGS(e) & HVhek_NOTSHARED)) {
HEK *
const
source = HeKEY_hek(e);
HEK *shared = (HEK*)ptr_table_fetch(PL_ptr_table, source);
if
(shared) {
(
void
)share_hek_hek(shared);
}
else
{
shared
= share_hek_flags(HEK_KEY(source), HEK_LEN(source),
HEK_HASH(source), HEK_FLAGS(source));
ptr_table_store(PL_ptr_table, source, shared);
}
HeKEY_hek(ret) = shared;
}
else
HeKEY_hek(ret) = save_hek_flags(HeKEY(e), HeKLEN(e), HeHASH(e),
HeKFLAGS(e));
HeVAL(ret) = sv_dup_inc(HeVAL(e), param);
HeNEXT(ret) = he_dup(HeNEXT(e), FALSE, param);
return
ret;
}
#endif /* USE_ITHREADS */
static
void
S_hv_notallowed(pTHX_
int
flags,
const
char
*key, I32 klen,
const
char
*msg)
{
SV *
const
sv = newSV_type_mortal(SVt_PV);
PERL_ARGS_ASSERT_HV_NOTALLOWED;
if
(!(flags & HVhek_FREEKEY)) {
sv_setpvn_fresh(sv, key, klen);
}
else
{
sv_usepvn(sv, (
char
*) key, klen);
}
if
(flags & HVhek_UTF8) {
SvUTF8_on(sv);
}
Perl_croak(aTHX_ msg, SVfARG(sv));
}
void
*
Perl_hv_common_key_len(pTHX_ HV *hv,
const
char
*key, I32 klen_i32,
const
int
action, SV *val,
const
U32 hash)
{
STRLEN klen;
int
flags;
PERL_ARGS_ASSERT_HV_COMMON_KEY_LEN;
if
(klen_i32 < 0) {
klen = -klen_i32;
flags = HVhek_UTF8;
}
else
{
klen = klen_i32;
flags = 0;
}
return
hv_common(hv, NULL, key, klen, flags, action, val, hash);
}
void
*
Perl_hv_common(pTHX_ HV *hv, SV *keysv,
const
char
*key, STRLEN klen,
int
flags,
int
action, SV *val, U32 hash)
{
XPVHV* xhv;
HE *entry;
HE **oentry;
SV *sv;
bool
is_utf8;
bool
in_collision;
const
int
return_svp = action & HV_FETCH_JUST_SV;
HEK *keysv_hek = NULL;
if
(!hv)
return
NULL;
if
(SvIS_FREED(hv))
return
NULL;
assert
(SvTYPE(hv) == SVt_PVHV);
if
(SvSMAGICAL(hv) && SvGMAGICAL(hv) && !(action & HV_DISABLE_UVAR_XKEY)) {
MAGIC* mg;
if
((mg = mg_find((
const
SV *)hv, PERL_MAGIC_uvar))) {
struct
ufuncs *
const
uf = (
struct
ufuncs *)mg->mg_ptr;
if
(uf->uf_set == NULL) {
SV* obj = mg->mg_obj;
if
(!keysv) {
keysv = newSVpvn_flags(key, klen, SVs_TEMP |
((flags & HVhek_UTF8)
? SVf_UTF8 : 0));
}
mg->mg_obj = keysv;
uf->uf_index = action;
magic_getuvar(MUTABLE_SV(hv), mg);
keysv = mg->mg_obj;
mg->mg_obj = obj;
hash = 0;
}
}
}
if
(keysv) {
if
(flags & HVhek_FREEKEY)
Safefree(key);
key = SvPV_const(keysv, klen);
is_utf8 = (SvUTF8(keysv) != 0);
if
(SvIsCOW_shared_hash(keysv)) {
flags = HVhek_KEYCANONICAL | (is_utf8 ? HVhek_UTF8 : 0);
}
else
{
flags = 0;
}
}
else
{
is_utf8 = cBOOL(flags & HVhek_UTF8);
flags &= ~HVhek_NOTSHARED;
}
if
(action & HV_DELETE) {
return
(
void
*) hv_delete_common(hv, keysv, key, klen,
flags | (is_utf8 ? HVhek_UTF8 : 0),
action, hash);
}
xhv = (XPVHV*)SvANY(hv);
if
(SvMAGICAL(hv)) {
if
(SvRMAGICAL(hv) && !(action & (HV_FETCH_ISSTORE|HV_FETCH_ISEXISTS))) {
if
(mg_find((
const
SV *)hv, PERL_MAGIC_tied)
|| SvGMAGICAL((
const
SV *)hv))
{
if
(!keysv) {
keysv = newSVpvn_utf8(key, klen, is_utf8);
}
else
{
keysv = newSVsv(keysv);
}
sv = sv_newmortal();
mg_copy(MUTABLE_SV(hv), sv, (
char
*)keysv, HEf_SVKEY);
entry = PL_hv_fetch_ent_mh;
if
(entry)
PL_hv_fetch_ent_mh = HeNEXT(entry);
else
{
char
*k;
entry = new_HE();
Newx(k, HEK_BASESIZE +
sizeof
(
const
SV *),
char
);
HeKEY_hek(entry) = (HEK*)k;
}
HeNEXT(entry) = NULL;
HeSVKEY_set(entry, keysv);
HeVAL(entry) = sv;
sv_upgrade(sv, SVt_PVLV);
LvTYPE(sv) =
'T'
;
LvTARG(sv) = MUTABLE_SV(entry);
if
(flags & HVhek_FREEKEY)
Safefree(key);
if
(return_svp) {
return
entry ? (
void
*) &HeVAL(entry) : NULL;
}
return
(
void
*) entry;
}
#ifdef ENV_IS_CASELESS
else
if
(mg_find((
const
SV *)hv, PERL_MAGIC_env)) {
U32 i;
for
(i = 0; i < klen; ++i)
if
(isLOWER(key[i])) {
const
char
*
const
nkey = strupr(savepvn(key,klen));
void
*result = hv_common(hv, NULL, nkey, klen,
HVhek_FREEKEY,
0
| HV_DISABLE_UVAR_XKEY
| return_svp,
NULL
,
0
);
if
(!result && (action & HV_FETCH_LVALUE)) {
result = hv_common(hv, keysv, key, klen, flags,
HV_FETCH_ISSTORE
| HV_DISABLE_UVAR_XKEY
| return_svp,
newSV_type(SVt_NULL), hash);
}
else
{
if
(flags & HVhek_FREEKEY)
Safefree(key);
}
return
result;
}
}
#endif
}
else
if
(SvRMAGICAL(hv) && (action & HV_FETCH_ISEXISTS)) {
if
(mg_find((
const
SV *)hv, PERL_MAGIC_tied)
|| SvGMAGICAL((
const
SV *)hv)) {
SV *
const
svret = sv_newmortal();
sv = sv_newmortal();
if
(keysv || is_utf8) {
if
(!keysv) {
keysv = newSVpvn_utf8(key, klen, TRUE);
}
else
{
keysv = newSVsv(keysv);
}
mg_copy(MUTABLE_SV(hv), sv, (
char
*)sv_2mortal(keysv), HEf_SVKEY);
}
else
{
mg_copy(MUTABLE_SV(hv), sv, key, klen);
}
if
(flags & HVhek_FREEKEY)
Safefree(key);
{
MAGIC *
const
mg = mg_find(sv, PERL_MAGIC_tiedelem);
if
(mg)
magic_existspack(svret, mg);
}
return
SvTRUE_NN(svret) ? (
void
*)hv : NULL;
}
#ifdef ENV_IS_CASELESS
else
if
(mg_find((
const
SV *)hv, PERL_MAGIC_env)) {
char
*
const
keysave = (
char
*
const
)key;
key = savepvn(key,klen);
key = (
const
char
*)strupr((
char
*)key);
is_utf8 = FALSE;
hash = 0;
keysv = 0;
if
(flags & HVhek_FREEKEY) {
Safefree(keysave);
}
flags |= HVhek_FREEKEY;
}
#endif
}
else
if
(action & HV_FETCH_ISSTORE) {
bool
needs_copy;
bool
needs_store;
hv_magic_check (hv, &needs_copy, &needs_store);
if
(needs_copy) {
const
bool
save_taint = TAINT_get;
if
(keysv || is_utf8) {
if
(!keysv) {
keysv = newSVpvn_utf8(key, klen, TRUE);
}
if
(TAINTING_get)
TAINT_set(SvTAINTED(keysv));
keysv = sv_2mortal(newSVsv(keysv));
mg_copy(MUTABLE_SV(hv), val, (
char
*)keysv, HEf_SVKEY);
}
else
{
mg_copy(MUTABLE_SV(hv), val, key, klen);
}
TAINT_IF(save_taint);
#ifdef NO_TAINT_SUPPORT
PERL_UNUSED_VAR(save_taint);
#endif
if
(!needs_store) {
if
(flags & HVhek_FREEKEY)
Safefree(key);
return
NULL;
}
#ifdef ENV_IS_CASELESS
else
if
(mg_find((
const
SV *)hv, PERL_MAGIC_env)) {
const
char
*keysave = key;
key = savepvn(key,klen);
key = (
const
char
*)strupr((
char
*)key);
is_utf8 = FALSE;
hash = 0;
keysv = 0;
if
(flags & HVhek_FREEKEY) {
Safefree(keysave);
}
flags |= HVhek_FREEKEY;
}
#endif
}
}
}
if
(!HvARRAY(hv)) {
if
((action & (HV_FETCH_LVALUE | HV_FETCH_ISSTORE))
#ifdef DYNAMIC_ENV_FETCH /* if it's an %ENV lookup, we may get it on the fly */
|| (SvRMAGICAL((
const
SV *)hv)
&& mg_find((
const
SV *)hv, PERL_MAGIC_env))
#endif
) {
char
*array;
Newxz(array,
PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1
),
char
);
HvARRAY(hv) = (HE**)array;
}
#ifdef DYNAMIC_ENV_FETCH
else
if
(action & HV_FETCH_ISEXISTS) {
}
#endif
else
{
if
(flags & HVhek_FREEKEY)
Safefree(key);
return
NULL;
}
}
if
(is_utf8 && !(flags & HVhek_KEYCANONICAL)) {
char
*
const
keysave = (
char
*)key;
key = (
char
*)bytes_from_utf8((U8*)key, &klen, &is_utf8);
if
(is_utf8)
flags |= HVhek_UTF8;
else
flags &= ~HVhek_UTF8;
if
(key != keysave) {
if
(flags & HVhek_FREEKEY)
Safefree(keysave);
flags |= HVhek_WASUTF8 | HVhek_FREEKEY;
hash = 0;
}
}
if
(keysv && (SvIsCOW_shared_hash(keysv))) {
if
(HvSHAREKEYS(hv))
keysv_hek = SvSHARED_HEK_FROM_PV(SvPVX_const(keysv));
hash = SvSHARED_HASH(keysv);
}
else
if
(!hash)
PERL_HASH(hash, key, klen);
#ifdef DYNAMIC_ENV_FETCH
if
(!HvARRAY(hv)) entry = NULL;
else
#endif
{
entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)];
}
if
(!entry)
goto
not_found;
if
(keysv_hek) {
int
keysv_flags = HEK_FLAGS(keysv_hek);
HE *orig_entry = entry;
for
(; entry; entry = HeNEXT(entry)) {
HEK *hek = HeKEY_hek(entry);
if
(hek == keysv_hek)
goto
found;
if
(HEK_FLAGS(hek) != keysv_flags)
break
;
}
if
(!entry)
goto
not_found;
entry = orig_entry;
}
for
(; entry; entry = HeNEXT(entry)) {
if
(HeHASH(entry) != hash)
continue
;
if
(HeKLEN(entry) != (I32)klen)
continue
;
if
(memNE(HeKEY(entry),key,klen))
continue
;
if
((HeKFLAGS(entry) ^ flags) & HVhek_UTF8)
continue
;
found:
if
(action & (HV_FETCH_LVALUE|HV_FETCH_ISSTORE)) {
if
((HeKFLAGS(entry) ^ flags) & HVhek_WASUTF8) {
if
((HeKFLAGS(entry) & HVhek_NOTSHARED) == 0) {
HEK *
const
new_hek
= share_hek_flags(key, klen, hash, flags & ~HVhek_FREEKEY);
unshare_hek (HeKEY_hek(entry));
HeKEY_hek(entry) = new_hek;
}
else
if
(hv == PL_strtab) {
if
(flags & HVhek_FREEKEY)
Safefree(key);
Perl_croak(aTHX_ S_strtab_error,
action & HV_FETCH_LVALUE ?
"fetch"
:
"store"
);
}
else
{
HeKFLAGS(entry) ^= HVhek_WASUTF8;
}
if
(flags & HVhek_ENABLEHVKFLAGS)
HvHASKFLAGS_on(hv);
}
if
(HeVAL(entry) == &PL_sv_placeholder) {
if
(action & HV_FETCH_LVALUE) {
if
(SvMAGICAL(hv)) {
break
;
}
val = newSV_type(SVt_NULL);
HvPLACEHOLDERS(hv)--;
}
else
{
if
(val != &PL_sv_placeholder)
HvPLACEHOLDERS(hv)--;
}
HeVAL(entry) = val;
}
else
if
(action & HV_FETCH_ISSTORE) {
SvREFCNT_dec(HeVAL(entry));
HeVAL(entry) = val;
}
}
else
if
(HeVAL(entry) == &PL_sv_placeholder) {
break
;
}
if
(flags & HVhek_FREEKEY)
Safefree(key);
if
(return_svp) {
return
(
void
*) &HeVAL(entry);
}
return
entry;
}
not_found:
#ifdef DYNAMIC_ENV_FETCH /* %ENV lookup? If so, try to fetch the value now */
if
(!(action & HV_FETCH_ISSTORE)
&& SvRMAGICAL((
const
SV *)hv)
&& mg_find((
const
SV *)hv, PERL_MAGIC_env)) {
unsigned
long
len;
const
char
*
const
env = PerlEnv_ENVgetenv_len(key,&len);
if
(env) {
sv = newSVpvn(env,len);
SvTAINTED_on(sv);
return
hv_common(hv, keysv, key, klen, flags,
HV_FETCH_ISSTORE|HV_DISABLE_UVAR_XKEY|return_svp,
sv, hash);
}
}
#endif
if
(!entry && SvREADONLY(hv) && !(action & HV_FETCH_ISEXISTS)) {
hv_notallowed(flags, key, klen,
"Attempt to access disallowed key '%"
SVf
"' in"
" a restricted hash"
);
}
if
(!(action & (HV_FETCH_LVALUE|HV_FETCH_ISSTORE))) {
if
(flags & HVhek_FREEKEY)
Safefree(key);
return
NULL;
}
if
(action & HV_FETCH_LVALUE) {
val = action & HV_FETCH_EMPTY_HE ? NULL : newSV_type(SVt_NULL);
if
(SvMAGICAL(hv)) {
return
hv_common(hv, keysv, key, klen, flags,
HV_FETCH_ISSTORE|HV_DISABLE_UVAR_XKEY|return_svp,
val, hash);
}
}
if
(!HvARRAY(hv)) {
char
*array;
Newxz(array,
PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1
),
char
);
HvARRAY(hv) = (HE**)array;
}
oentry = &(HvARRAY(hv))[hash & (I32) xhv->xhv_max];
if
(LIKELY(HvSHAREKEYS(hv))) {
entry = new_HE();
HeKEY_hek(entry) = share_hek_flags(key, klen, hash, flags);
}
else
if
(UNLIKELY(hv == PL_strtab)) {
if
(flags & HVhek_FREEKEY)
Safefree(key);
Perl_croak(aTHX_ S_strtab_error,
action & HV_FETCH_LVALUE ?
"fetch"
:
"store"
);
}
else
{
entry = new_HE();
HeKEY_hek(entry) = save_hek_flags(key, klen, hash, flags);
}
HeVAL(entry) = val;
in_collision = cBOOL(*oentry != NULL);
#ifdef PERL_HASH_RANDOMIZE_KEYS
if
( *oentry && PL_HASH_RAND_BITS_ENABLED) {
UPDATE_HASH_RAND_BITS_KEY(key,klen);
if
( PL_hash_rand_bits & 1 ) {
HeNEXT(entry) = HeNEXT(*oentry);
HeNEXT(*oentry) = entry;
}
else
{
HeNEXT(entry) = *oentry;
*oentry = entry;
}
}
else
#endif
{
HeNEXT(entry) = *oentry;
*oentry = entry;
}
#ifdef PERL_HASH_RANDOMIZE_KEYS
if
(HvHasAUX(hv)) {
MAYBE_UPDATE_HASH_RAND_BITS_KEY(key,klen);
HvAUX(hv)->xhv_rand= (U32)PL_hash_rand_bits;
}
#endif
if
(val == &PL_sv_placeholder)
HvPLACEHOLDERS(hv)++;
if
(flags & HVhek_ENABLEHVKFLAGS)
HvHASKFLAGS_on(hv);
xhv->xhv_keys++;
if
( in_collision && DO_HSPLIT(xhv) ) {
const
STRLEN oldsize = xhv->xhv_max + 1;
const
U32 items = (U32)HvPLACEHOLDERS_get(hv);
if
(items
&& !SvREADONLY(hv)
) {
clear_placeholders(hv, items);
if
(DO_HSPLIT(xhv))
hsplit(hv, oldsize, oldsize * 2);
}
else
hsplit(hv, oldsize, oldsize * 2);
}
if
(return_svp) {
return
entry ? (
void
*) &HeVAL(entry) : NULL;
}
return
(
void
*) entry;
}
STATIC
void
S_hv_magic_check(HV *hv,
bool
*needs_copy,
bool
*needs_store)
{
const
MAGIC *mg = SvMAGIC(hv);
PERL_ARGS_ASSERT_HV_MAGIC_CHECK;
*needs_copy = FALSE;
*needs_store = TRUE;
while
(mg) {
if
(isUPPER(mg->mg_type)) {
*needs_copy = TRUE;
if
(mg->mg_type == PERL_MAGIC_tied) {
*needs_store = FALSE;
return
;
}
}
mg = mg->mg_moremagic;
}
}
SV *
Perl_hv_scalar(pTHX_ HV *hv)
{
SV *sv;
UV u;
PERL_ARGS_ASSERT_HV_SCALAR;
if
(SvRMAGICAL(hv)) {
MAGIC *
const
mg = mg_find((
const
SV *)hv, PERL_MAGIC_tied);
if
(mg)
return
magic_scalarpack(hv, mg);
}
sv = newSV_type_mortal(SVt_IV);
u = HvUSEDKEYS(hv);
if
(u <= (UV)IV_MAX) {
SvIV_set(sv, (IV)u);
(
void
)SvIOK_only(sv);
SvTAINT(sv);
}
else
{
SvIV_set(sv, 0);
SvUV_set(sv, u);
(
void
)SvIOK_only_UV(sv);
SvTAINT(sv);
}
return
sv;
}
void
Perl_hv_pushkv(pTHX_ HV *hv, U32 flags)
{
HE *entry;
bool
tied = SvRMAGICAL(hv) && (mg_find(MUTABLE_SV(hv), PERL_MAGIC_tied)
#ifdef DYNAMIC_ENV_FETCH /* might not know number of keys yet */
|| mg_find(MUTABLE_SV(hv), PERL_MAGIC_env)
#endif
);
PERL_ARGS_ASSERT_HV_PUSHKV;
assert
(flags);
(
void
)hv_iterinit(hv);
if
(tied) {
SSize_t ext = (flags == 3) ? 2 : 1;
while
((entry = hv_iternext(hv))) {
rpp_extend(ext);
if
(flags & 1)
rpp_push_1(hv_iterkeysv(entry));
if
(flags & 2)
rpp_push_1(hv_iterval(hv, entry));
}
}
else
{
Size_t nkeys = HvUSEDKEYS(hv);
SSize_t ext;
if
(!nkeys)
return
;
assert
(nkeys <= (SSize_t_MAX >> 1));
ext = nkeys * ((flags == 3) ? 2 : 1);
EXTEND_MORTAL(nkeys);
rpp_extend(ext);
while
((entry = hv_iternext(hv))) {
if
(flags & 1) {
SV *keysv = newSVhek(HeKEY_hek(entry));
SvTEMP_on(keysv);
PL_tmps_stack[++PL_tmps_ix] = keysv;
rpp_push_1(keysv);
}
if
(flags & 2)
rpp_push_1(HeVAL(entry));
}
}
}
SV *
Perl_hv_bucket_ratio(pTHX_ HV *hv)
{
SV *sv;
PERL_ARGS_ASSERT_HV_BUCKET_RATIO;
if
(SvRMAGICAL(hv)) {
MAGIC *
const
mg = mg_find((
const
SV *)hv, PERL_MAGIC_tied);
if
(mg)
return
magic_scalarpack(hv, mg);
}
if
(HvUSEDKEYS((HV *)hv)) {
sv = sv_newmortal();
Perl_sv_setpvf(aTHX_ sv,
"%ld/%ld"
,
(
long
)HvFILL(hv), (
long
)HvMAX(hv) + 1);
}
else
sv = &PL_sv_zero;
return
sv;
}
STATIC SV *
S_hv_delete_common(pTHX_ HV *hv, SV *keysv,
const
char
*key, STRLEN klen,
int
k_flags, I32 d_flags, U32 hash)
{
XPVHV* xhv;
HE *entry;
HE **oentry;
HE **first_entry;
bool
is_utf8 = cBOOL(k_flags & HVhek_UTF8);
HEK *keysv_hek = NULL;
U8 mro_changes = 0;
SV *sv;
GV *gv = NULL;
HV *stash = NULL;
if
(SvMAGICAL(hv)) {
bool
needs_copy;
bool
needs_store;
hv_magic_check (hv, &needs_copy, &needs_store);
if
(needs_copy) {
SV *sv;
entry = (HE *) hv_common(hv, keysv, key, klen,
k_flags & ~HVhek_FREEKEY,
HV_FETCH_LVALUE|HV_DISABLE_UVAR_XKEY,
NULL, hash);
sv = entry ? HeVAL(entry) : NULL;
if
(sv) {
if
(SvMAGICAL(sv)) {
mg_clear(sv);
}
if
(!needs_store) {
if
(mg_find(sv, PERL_MAGIC_tiedelem)) {
sv_unmagic(sv, PERL_MAGIC_tiedelem);
return
sv;
}
return
NULL;
}
#ifdef ENV_IS_CASELESS
else
if
(mg_find((
const
SV *)hv, PERL_MAGIC_env)) {
keysv = newSVpvn_flags(key, klen, SVs_TEMP);
if
(k_flags & HVhek_FREEKEY) {
Safefree(key);
}
key = strupr(SvPVX(keysv));
is_utf8 = 0;
k_flags = 0;
hash = 0;
}
#endif
}
}
}
xhv = (XPVHV*)SvANY(hv);
if
(!HvTOTALKEYS(hv))
return
NULL;
if
(is_utf8 && !(k_flags & HVhek_KEYCANONICAL)) {
const
char
*
const
keysave = key;
key = (
char
*)bytes_from_utf8((U8*)key, &klen, &is_utf8);
if
(is_utf8)
k_flags |= HVhek_UTF8;
else
k_flags &= ~HVhek_UTF8;
if
(key != keysave) {
if
(k_flags & HVhek_FREEKEY) {
Safefree(keysave);
}
k_flags |= HVhek_WASUTF8 | HVhek_FREEKEY;
}
}
if
(keysv && (SvIsCOW_shared_hash(keysv))) {
if
(HvSHAREKEYS(hv))
keysv_hek = SvSHARED_HEK_FROM_PV(SvPVX_const(keysv));
hash = SvSHARED_HASH(keysv);
}
else
if
(!hash)
PERL_HASH(hash, key, klen);
first_entry = oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)];
entry = *oentry;
if
(!entry)
goto
not_found;
if
(keysv_hek) {
int
keysv_flags = HEK_FLAGS(keysv_hek);
for
(; entry; oentry = &HeNEXT(entry), entry = *oentry) {
HEK *hek = HeKEY_hek(entry);
if
(hek == keysv_hek)
goto
found;
if
(HEK_FLAGS(hek) != keysv_flags)
break
;
}
if
(!entry)
goto
not_found;
oentry = first_entry;
entry = *oentry;
}
for
(; entry; oentry = &HeNEXT(entry), entry = *oentry) {
if
(HeHASH(entry) != hash)
continue
;
if
(HeKLEN(entry) != (I32)klen)
continue
;
if
(memNE(HeKEY(entry),key,klen))
continue
;
if
((HeKFLAGS(entry) ^ k_flags) & HVhek_UTF8)
continue
;
found:
if
(hv == PL_strtab) {
if
(k_flags & HVhek_FREEKEY)
Safefree(key);
Perl_croak(aTHX_ S_strtab_error,
"delete"
);
}
sv = HeVAL(entry);
if
(sv == &PL_sv_placeholder) {
if
(k_flags & HVhek_FREEKEY)
Safefree(key);
return
NULL;
}
if
(SvREADONLY(hv) && sv && SvREADONLY(sv)) {
hv_notallowed(k_flags, key, klen,
"Attempt to delete readonly key '%"
SVf
"' from"
" a restricted hash"
);
}
if
(SvREADONLY(hv)) {
HeVAL(entry) = &PL_sv_placeholder;
HvPLACEHOLDERS(hv)++;
}
else
{
HeVAL(entry) = NULL;
*oentry = HeNEXT(entry);
if
(HvHasAUX(hv) && entry == HvAUX(hv)->xhv_eiter
) {
HvLAZYDEL_on(hv);
}
else
{
if
(HvHasAUX(hv) && HvLAZYDEL(hv) &&
entry == HeNEXT(HvAUX(hv)->xhv_eiter))
HeNEXT(HvAUX(hv)->xhv_eiter) = HeNEXT(entry);
hv_free_ent(NULL, entry);
}
xhv->xhv_keys--;
if
(xhv->xhv_keys == 0)
HvHASKFLAGS_off(hv);
}
if
(sv && SvTYPE(sv) == SVt_PVGV && HvHasENAME(hv)) {
gv = (GV *)sv;
if
((
(klen > 1 && key[klen-2] ==
':'
&& key[klen-1] ==
':'
)
||
(klen == 1 && key[0] ==
':'
)
)
&& (klen != 6 || hv!=PL_defstash || memNE(key,
"main::"
,6))
&& (stash = GvHV((GV *)gv))
&& HvHasENAME(stash)) {
mro_changes = 2;
SvREFCNT_inc_simple_void_NN(
sv_2mortal((SV *)gv)
);
}
else
if
(memEQs(key, klen,
"ISA"
) && GvAV(gv)) {
AV *isa = GvAV(gv);
MAGIC *mg = mg_find((SV*)isa, PERL_MAGIC_isa);
mro_changes = 1;
if
(mg) {
if
(mg->mg_obj == (SV*)gv) {
SV **svp, **end;
strip_magic:
svp = AvARRAY(isa);
if
(svp) {
end = svp + (AvFILLp(isa)+1);
while
(svp < end) {
if
(*svp)
mg_free_type(*svp, PERL_MAGIC_isaelem);
++svp;
}
}
mg_free_type((SV*)GvAV(gv), PERL_MAGIC_isa);
}
else
{
AV *av = (AV*)mg->mg_obj;
SV **svp, **arrayp;
SSize_t index;
SSize_t items;
assert
(SvTYPE(mg->mg_obj) == SVt_PVAV);
arrayp = svp = AvARRAY(av);
items = AvFILLp(av) + 1;
if
(items == 1) {
assert
(*arrayp == (SV *)gv);
mg->mg_obj = NULL;
AvFILLp(av) = -1;
SvREFCNT_dec_NN(av);
goto
strip_magic;
}
else
{
while
(items--) {
if
(*svp == (SV*)gv)
break
;
++svp;
}
index = svp - arrayp;
assert
(index >= 0 && index <= AvFILLp(av));
if
(index < AvFILLp(av)) {
arrayp[index] = arrayp[AvFILLp(av)];
}
arrayp[AvFILLp(av)] = NULL;
--AvFILLp(av);
}
}
}
}
}
if
(k_flags & HVhek_FREEKEY)
Safefree(key);
if
(sv) {
if
(isGV(sv) && isGV_with_GP(sv) && GvCVu(sv)
&& HvHasENAME(hv))
mro_method_changed_in(hv);
if
(d_flags & G_DISCARD) {
SvREFCNT_dec(sv);
sv = NULL;
}
else
{
sv_2mortal(sv);
}
}
if
(mro_changes == 1) mro_isa_changed_in(hv);
else
if
(mro_changes == 2)
mro_package_moved(NULL, stash, gv, 1);
return
sv;
}
not_found:
if
(SvREADONLY(hv)) {
hv_notallowed(k_flags, key, klen,
"Attempt to delete disallowed key '%"
SVf
"' from"
" a restricted hash"
);
}
if
(k_flags & HVhek_FREEKEY)
Safefree(key);
return
NULL;
}
#ifdef PERL_USE_UNSHARED_KEYS_IN_LARGE_HASHES
static
bool
S_large_hash_heuristic(pTHX_ HV *hv, STRLEN size) {
if
(size > 42
&& !SvOBJECT(hv)
&& !(HvHasAUX(hv) && HvENAME_get(hv))) {
return
TRUE;
}
return
FALSE;
}
#endif
STATIC
void
S_hsplit(pTHX_ HV *hv, STRLEN
const
oldsize, STRLEN newsize)
{
STRLEN i = 0;
char
*a = (
char
*) HvARRAY(hv);
HE **aep;
PERL_ARGS_ASSERT_HSPLIT;
if
(newsize > MAX_BUCKET_MAX+1)
return
;
PL_nomemok = TRUE;
Renew(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize),
char
);
PL_nomemok = FALSE;
if
(!a) {
return
;
}
#ifdef PERL_HASH_RANDOMIZE_KEYS
MAYBE_UPDATE_HASH_RAND_BITS();
#endif
HvARRAY(hv) = (HE**) a;
HvMAX(hv) = newsize - 1;
Zero(&a[oldsize *
sizeof
(HE*)], (newsize-oldsize) *
sizeof
(HE*),
char
);
if
(!HvTOTALKEYS(hv))
return
;
if
(LARGE_HASH_HEURISTIC(hv, HvTOTALKEYS(hv)))
HvSHAREKEYS_off(hv);
newsize--;
aep = (HE**)a;
do
{
HE **oentry = aep + i;
HE *entry = aep[i];
if
(!entry)
continue
;
do
{
U32 j = (HeHASH(entry) & newsize);
if
(j != (U32)i) {
*oentry = HeNEXT(entry);
#ifdef PERL_HASH_RANDOMIZE_KEYS
if
(aep[j] && PL_HASH_RAND_BITS_ENABLED) {
UPDATE_HASH_RAND_BITS();
if
(PL_hash_rand_bits & 1) {
HeNEXT(entry)= HeNEXT(aep[j]);
HeNEXT(aep[j])= entry;
}
else
{
HeNEXT(entry) = aep[j];
aep[j] = entry;
}
}
else
#endif
{
HeNEXT(entry) = aep[j];
aep[j] = entry;
}
}
else
{
oentry = &HeNEXT(entry);
}
entry = *oentry;
}
while
(entry);
}
while
(i++ < oldsize);
}
void
Perl_hv_ksplit(pTHX_ HV *hv, IV newmax)
{
XPVHV* xhv = (XPVHV*)SvANY(hv);
const
I32 oldsize = (I32) xhv->xhv_max+1;
I32 newsize;
I32 wantsize;
I32 trysize;
char
*a;
PERL_ARGS_ASSERT_HV_KSPLIT;
wantsize = (I32) newmax;
if
(wantsize != newmax)
return
;
wantsize= wantsize + (wantsize >> 1);
if
(wantsize < newmax)
return
;
newsize = oldsize;
while
(wantsize > newsize) {
trysize = newsize << 1;
if
(trysize > newsize) {
newsize = trysize;
}
else
{
return
;
}
}
if
(newsize <= oldsize)
return
;
a = (
char
*) HvARRAY(hv);
if
(a) {
#ifdef PERL_HASH_RANDOMIZE_KEYS
U32 was_ook = HvHasAUX(hv);
#endif
hsplit(hv, oldsize, newsize);
#ifdef PERL_HASH_RANDOMIZE_KEYS
if
(was_ook && HvHasAUX(hv) && HvTOTALKEYS(hv)) {
MAYBE_UPDATE_HASH_RAND_BITS();
HvAUX(hv)->xhv_rand = (U32)PL_hash_rand_bits;
}
#endif
}
else
{
if
(LARGE_HASH_HEURISTIC(hv, newmax))
HvSHAREKEYS_off(hv);
Newxz(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize),
char
);
xhv->xhv_max = newsize - 1;
HvARRAY(hv) = (HE **) a;
}
}
#define HV_SET_MAX_ADJUSTED_FOR_KEYS(hv,hv_max,hv_keys) STMT_START {\
\
if
(hv_max < PERL_HASH_DEFAULT_HvMAX) { \
hv_max = PERL_HASH_DEFAULT_HvMAX; \
}
else
{ \
while
(hv_max > PERL_HASH_DEFAULT_HvMAX && hv_max + 1 >= hv_keys * 2) \
hv_max = hv_max / 2; \
} \
HvMAX(hv) = hv_max; \
} STMT_END
HV *
Perl_newHVhv(pTHX_ HV *ohv)
{
HV *
const
hv = newHV();
STRLEN hv_max;
if
(!ohv || (!HvTOTALKEYS(ohv) && !SvMAGICAL((
const
SV *)ohv)))
return
hv;
hv_max = HvMAX(ohv);
if
(!SvMAGICAL((
const
SV *)ohv)) {
STRLEN i;
HE **ents, **
const
oents = (HE **)HvARRAY(ohv);
char
*a;
Newx(a, PERL_HV_ARRAY_ALLOC_BYTES(hv_max+1),
char
);
ents = (HE**)a;
if
(HvSHAREKEYS(ohv)) {
#ifdef NODEFAULT_SHAREKEYS
HvSHAREKEYS_on(hv);
#else
assert
(HvSHAREKEYS(hv));
#endif
}
else
{
HvSHAREKEYS_off(hv);
}
for
(i = 0; i <= hv_max; i++) {
HE *prev = NULL;
HE *oent = oents[i];
if
(!oent) {
ents[i] = NULL;
continue
;
}
for
(; oent; oent = HeNEXT(oent)) {
HE *
const
ent = new_HE();
SV *
const
val = HeVAL(oent);
const
int
flags = HeKFLAGS(oent);
HeVAL(ent) = SvIMMORTAL(val) ? val : newSVsv(val);
if
((flags & HVhek_NOTSHARED) == 0) {
HeKEY_hek(ent) = share_hek_hek(HeKEY_hek(oent));
}
else
{
const
U32 hash = HeHASH(oent);
const
char
*
const
key = HeKEY(oent);
const
STRLEN len = HeKLEN(oent);
HeKEY_hek(ent) = save_hek_flags(key, len, hash, flags);
}
if
(prev)
HeNEXT(prev) = ent;
else
ents[i] = ent;
prev = ent;
HeNEXT(ent) = NULL;
}
}
HvMAX(hv) = hv_max;
HvTOTALKEYS(hv) = HvTOTALKEYS(ohv);
HvARRAY(hv) = ents;
}
else
{
HE *entry;
const
I32 riter = HvRITER_get(ohv);
HE *
const
eiter = HvEITER_get(ohv);
STRLEN hv_keys = HvTOTALKEYS(ohv);
HV_SET_MAX_ADJUSTED_FOR_KEYS(hv,hv_max,hv_keys);
hv_iterinit(ohv);
while
((entry = hv_iternext_flags(ohv, 0))) {
SV *val = hv_iterval(ohv,entry);
SV *
const
keysv = HeSVKEY(entry);
val = SvIMMORTAL(val) ? val : newSVsv(val);
if
(keysv)
(
void
)hv_store_ent(hv, keysv, val, 0);
else
(
void
)hv_store_flags(hv, HeKEY(entry), HeKLEN(entry), val,
HeHASH(entry), HeKFLAGS(entry));
}
HvRITER_set(ohv, riter);
HvEITER_set(ohv, eiter);
}
return
hv;
}
HV *
Perl_hv_copy_hints_hv(pTHX_ HV *
const
ohv)
{
HV *
const
hv = newHV();
if
(ohv) {
STRLEN hv_max = HvMAX(ohv);
STRLEN hv_keys = HvTOTALKEYS(ohv);
HE *entry;
const
I32 riter = HvRITER_get(ohv);
HE *
const
eiter = HvEITER_get(ohv);
ENTER;
SAVEFREESV(hv);
HV_SET_MAX_ADJUSTED_FOR_KEYS(hv,hv_max,hv_keys);
hv_iterinit(ohv);
while
((entry = hv_iternext_flags(ohv, 0))) {
SV *
const
sv = newSVsv(hv_iterval(ohv,entry));
SV *heksv = HeSVKEY(entry);
if
(!heksv && sv) heksv = newSVhek(HeKEY_hek(entry));
if
(sv) sv_magic(sv, NULL, PERL_MAGIC_hintselem,
(
char
*)heksv, HEf_SVKEY);
if
(heksv == HeSVKEY(entry))
(
void
)hv_store_ent(hv, heksv, sv, 0);
else
{
(
void
)hv_common(hv, heksv, HeKEY(entry), HeKLEN(entry),
HeKFLAGS(entry), HV_FETCH_ISSTORE|HV_FETCH_JUST_SV, sv, HeHASH(entry));
SvREFCNT_dec_NN(heksv);
}
}
HvRITER_set(ohv, riter);
HvEITER_set(ohv, eiter);
SvREFCNT_inc_simple_void_NN(hv);
LEAVE;
}
hv_magic(hv, NULL, PERL_MAGIC_hints);
return
hv;
}
#undef HV_SET_MAX_ADJUSTED_FOR_KEYS
STATIC SV*
S_hv_free_ent_ret(pTHX_ HE *entry)
{
PERL_ARGS_ASSERT_HV_FREE_ENT_RET;
SV *val = HeVAL(entry);
if
(HeKLEN(entry) == HEf_SVKEY) {
SvREFCNT_dec(HeKEY_sv(entry));
Safefree(HeKEY_hek(entry));
}
else
if
((HeKFLAGS(entry) & HVhek_NOTSHARED) == 0) {
unshare_hek(HeKEY_hek(entry));
}
else
{
Safefree(HeKEY_hek(entry));
}
del_HE(entry);
return
val;
}
void
Perl_hv_free_ent(pTHX_ HV *notused, HE *entry)
{
PERL_UNUSED_ARG(notused);
if
(!entry)
return
;
SV *val = hv_free_ent_ret(entry);
SvREFCNT_dec(val);
}
void
Perl_hv_delayfree_ent(pTHX_ HV *notused, HE *entry)
{
PERL_UNUSED_ARG(notused);
if
(!entry)
return
;
sv_2mortal(SvREFCNT_inc(HeVAL(entry)));
if
(HeKLEN(entry) == HEf_SVKEY) {
sv_2mortal(SvREFCNT_inc(HeKEY_sv(entry)));
}
hv_free_ent(NULL, entry);
}
void
Perl_hv_clear(pTHX_ HV *hv)
{
SSize_t orig_ix;
if
(!hv)
return
;
DEBUG_A(Perl_hv_assert(aTHX_ hv));
EXTEND_MORTAL(1);
PL_tmps_stack[++PL_tmps_ix] = SvREFCNT_inc_simple_NN(hv);
orig_ix = PL_tmps_ix;
if
(SvREADONLY(hv) && HvTOTALKEYS(hv)) {
STRLEN max = HvMAX(hv);
STRLEN i;
for
(i = 0; i <= max; i++) {
HE *entry = (HvARRAY(hv))[i];
for
(; entry; entry = HeNEXT(entry)) {
if
(HeVAL(entry) != &PL_sv_placeholder) {
if
(HeVAL(entry)) {
if
(SvREADONLY(HeVAL(entry))) {
SV*
const
keysv = hv_iterkeysv(entry);
Perl_croak_nocontext(
"Attempt to delete readonly key '%"
SVf
"' from a restricted hash"
,
(
void
*)keysv);
}
SvREFCNT_dec_NN(HeVAL(entry));
}
HeVAL(entry) = &PL_sv_placeholder;
HvPLACEHOLDERS(hv)++;
}
}
}
}
else
{
hv_free_entries(hv);
HvPLACEHOLDERS_set(hv, 0);
if
(SvRMAGICAL(hv))
mg_clear(MUTABLE_SV(hv));
HvHASKFLAGS_off(hv);
}
if
(HvHasAUX(hv)) {
if
(HvENAME_get(hv))
mro_isa_changed_in(hv);
HvEITER_set(hv, NULL);
}
if
(LIKELY(PL_tmps_ix == orig_ix))
PL_tmps_ix--;
else
PL_tmps_stack[orig_ix] = &PL_sv_undef;
SvREFCNT_dec_NN(hv);
}
void
Perl_hv_clear_placeholders(pTHX_ HV *hv)
{
const
U32 items = (U32)HvPLACEHOLDERS_get(hv);
PERL_ARGS_ASSERT_HV_CLEAR_PLACEHOLDERS;
if
(items)
clear_placeholders(hv, items);
}
static
void
S_clear_placeholders(pTHX_ HV *hv,
const
U32 placeholders)
{
I32 i;
U32 to_find = placeholders;
PERL_ARGS_ASSERT_CLEAR_PLACEHOLDERS;
assert
(to_find);
i = HvMAX(hv);
do
{
HE **oentry = &(HvARRAY(hv))[i];
HE *entry;
while
((entry = *oentry)) {
if
(HeVAL(entry) == &PL_sv_placeholder) {
*oentry = HeNEXT(entry);
if
(entry == HvEITER_get(hv))
HvLAZYDEL_on(hv);
else
{
if
(HvHasAUX(hv) && HvLAZYDEL(hv) &&
entry == HeNEXT(HvAUX(hv)->xhv_eiter))
HeNEXT(HvAUX(hv)->xhv_eiter) = HeNEXT(entry);
hv_free_ent(NULL, entry);
}
if
(--to_find == 0) {
HvTOTALKEYS(hv) -= (IV)placeholders;
if
(HvTOTALKEYS(hv) == 0)
HvHASKFLAGS_off(hv);
HvPLACEHOLDERS_set(hv, 0);
return
;
}
}
else
{
oentry = &HeNEXT(entry);
}
}
}
while
(--i >= 0);
assert
(to_find == 0);
NOT_REACHED;
}
STATIC
void
S_hv_free_entries(pTHX_ HV *hv)
{
STRLEN index = 0;
SV *sv;
PERL_ARGS_ASSERT_HV_FREE_ENTRIES;
while
((sv = Perl_hfree_next_entry(aTHX_ hv, &index)) || HvTOTALKEYS(hv)) {
SvREFCNT_dec(sv);
}
}
SV*
Perl_hfree_next_entry(pTHX_ HV *hv, STRLEN *indexp)
{
struct
xpvhv_aux *iter;
HE *entry;
HE ** array;
#ifdef DEBUGGING
STRLEN orig_index = *indexp;
#endif
PERL_ARGS_ASSERT_HFREE_NEXT_ENTRY;
if
(HvHasAUX(hv) && ((iter = HvAUX(hv)))) {
if
((entry = iter->xhv_eiter)) {
if
(entry && HvLAZYDEL(hv)) {
HvLAZYDEL_off(hv);
hv_free_ent(NULL, entry);
}
iter->xhv_riter = -1;
iter->xhv_eiter = NULL;
#ifdef PERL_HASH_RANDOMIZE_KEYS
iter->xhv_last_rand = iter->xhv_rand;
#endif
}
}
if
(!((XPVHV*)SvANY(hv))->xhv_keys)
return
NULL;
array = HvARRAY(hv);
assert
(array);
while
( ! ((entry = array[*indexp])) ) {
if
((*indexp)++ >= HvMAX(hv))
*indexp = 0;
assert
(*indexp != orig_index);
}
array[*indexp] = HeNEXT(entry);
((XPVHV*) SvANY(hv))->xhv_keys--;
if
( PL_phase != PERL_PHASE_DESTRUCT && HvHasENAME(hv)
&& HeVAL(entry) && isGV(HeVAL(entry))
&& GvHV(HeVAL(entry)) && HvHasENAME(GvHV(HeVAL(entry)))
) {
STRLEN klen;
const
char
*
const
key = HePV(entry,klen);
if
((klen > 1 && key[klen-1]==
':'
&& key[klen-2]==
':'
)
|| (klen == 1 && key[0] ==
':'
)) {
mro_package_moved(
NULL, GvHV(HeVAL(entry)),
(GV *)HeVAL(entry), 0
);
}
}
return
hv_free_ent_ret(entry);
}
void
Perl_hv_undef_flags(pTHX_ HV *hv, U32 flags)
{
bool
save;
SSize_t orig_ix = PL_tmps_ix;
if
(!hv)
return
;
save = cBOOL(SvREFCNT(hv));
DEBUG_A(Perl_hv_assert(aTHX_ hv));
if
(PL_phase != PERL_PHASE_DESTRUCT && HvHasNAME(hv)) {
if
(PL_stashcache) {
HEK *hek = HvNAME_HEK(hv);
DEBUG_o(Perl_deb(aTHX_
"hv_undef_flags clearing PL_stashcache for '%"
HEKf
"'\n"
, HEKfARG(hek)));
(
void
)hv_deletehek(PL_stashcache, hek, G_DISCARD);
}
hv_name_set(hv, NULL, 0, 0);
}
if
(save) {
EXTEND_MORTAL(1);
PL_tmps_stack[++PL_tmps_ix] = SvREFCNT_inc_simple_NN(hv);
orig_ix = PL_tmps_ix;
}
hv_free_entries(hv);
if
(HvHasAUX(hv)) {
struct
xpvhv_aux *aux = HvAUX(hv);
struct
mro_meta *meta;
const
char
*name;
if
(HvHasENAME(hv)) {
if
(PL_phase != PERL_PHASE_DESTRUCT)
mro_isa_changed_in(hv);
if
(PL_stashcache) {
DEBUG_o(Perl_deb(aTHX_
"hv_undef_flags clearing PL_stashcache for effective name '%"
HEKf
"'\n"
, HEKfARG(HvENAME_HEK_NN(hv))));
(
void
)hv_deletehek(PL_stashcache, HvENAME_HEK_NN(hv), G_DISCARD);
}
}
name = HvNAME(hv);
if
(flags & HV_NAME_SETALL
? cBOOL(aux->xhv_name_u.xhvnameu_name)
: cBOOL(name))
{
if
(name && PL_stashcache) {
DEBUG_o(Perl_deb(aTHX_
"hv_undef_flags clearing PL_stashcache for name '%"
HEKf
"'\n"
, HEKfARG(HvNAME_HEK_NN(hv))));
(
void
)hv_deletehek(PL_stashcache, HvNAME_HEK_NN(hv), G_DISCARD);
}
hv_name_set(hv, NULL, 0, flags);
}
if
((meta = aux->xhv_mro_meta)) {
if
(meta->mro_linear_all) {
SvREFCNT_dec_NN(meta->mro_linear_all);
}
else
SvREFCNT_dec(meta->mro_linear_current);
SvREFCNT_dec(meta->mro_nextmethod);
SvREFCNT_dec(meta->isa);
SvREFCNT_dec(meta->super);
Safefree(meta);
aux->xhv_mro_meta = NULL;
}
if
(HvSTASH_IS_CLASS(hv)) {
SvREFCNT_dec(aux->xhv_class_superclass);
SvREFCNT_dec(aux->xhv_class_initfields_cv);
SvREFCNT_dec(aux->xhv_class_adjust_blocks);
if
(aux->xhv_class_fields)
PadnamelistREFCNT_dec(aux->xhv_class_fields);
SvREFCNT_dec(aux->xhv_class_param_map);
Safefree(aux->xhv_class_suspended_initfields_compcv);
aux->xhv_class_suspended_initfields_compcv = NULL;
aux->xhv_aux_flags &= ~HvAUXf_IS_CLASS;
}
}
Safefree(HvARRAY(hv));
HvMAX(hv) = PERL_HASH_DEFAULT_HvMAX;
HvARRAY(hv) = 0;
if
(SvREFCNT(hv))
HvPLACEHOLDERS_set(hv, 0);
if
(SvRMAGICAL(hv))
mg_clear(MUTABLE_SV(hv));
if
(save) {
if
(LIKELY(PL_tmps_ix == orig_ix))
PL_tmps_ix--;
else
PL_tmps_stack[orig_ix] = &PL_sv_undef;
SvREFCNT_dec_NN(hv);
}
}
STRLEN
Perl_hv_fill(pTHX_ HV *
const
hv)
{
STRLEN count = 0;
HE **ents = HvARRAY(hv);
PERL_UNUSED_CONTEXT;
PERL_ARGS_ASSERT_HV_FILL;
if
(HvTOTALKEYS(hv) < 2)
return
HvTOTALKEYS(hv);
if
(ents) {
HE *
const
*
const
last = ents + HvMAX(hv);
count = last + 1 - ents;
do
{
if
(!*ents)
--count;
}
while
(++ents <= last);
}
return
count;
}
static
struct
xpvhv_aux*
S_hv_auxinit(pTHX_ HV *hv) {
struct
xpvhv_aux *iter;
PERL_ARGS_ASSERT_HV_AUXINIT;
if
(!HvHasAUX(hv)) {
char
*array = (
char
*) HvARRAY(hv);
if
(!array) {
Newxz(array, PERL_HV_ARRAY_ALLOC_BYTES(HvMAX(hv) + 1),
char
);
HvARRAY(hv) = (HE**)array;
}
iter = Perl_hv_auxalloc(aTHX_ hv);
#ifdef PERL_HASH_RANDOMIZE_KEYS
MAYBE_UPDATE_HASH_RAND_BITS();
iter->xhv_rand = (U32)PL_hash_rand_bits;
#endif
}
else
{
iter = HvAUX(hv);
}
iter->xhv_riter = -1;
iter->xhv_eiter = NULL;
#ifdef PERL_HASH_RANDOMIZE_KEYS
iter->xhv_last_rand = iter->xhv_rand;
#endif
iter->xhv_name_u.xhvnameu_name = 0;
iter->xhv_name_count = 0;
iter->xhv_backreferences = 0;
iter->xhv_mro_meta = NULL;
iter->xhv_aux_flags = 0;
return
iter;
}
I32
Perl_hv_iterinit(pTHX_ HV *hv)
{
PERL_ARGS_ASSERT_HV_ITERINIT;
if
(HvHasAUX(hv)) {
struct
xpvhv_aux * iter = HvAUX(hv);
HE *
const
entry = iter->xhv_eiter;
if
(entry && HvLAZYDEL(hv)) {
HvLAZYDEL_off(hv);
hv_free_ent(NULL, entry);
}
iter->xhv_riter = -1;
iter->xhv_eiter = NULL;
#ifdef PERL_HASH_RANDOMIZE_KEYS
iter->xhv_last_rand = iter->xhv_rand;
#endif
}
else
{
hv_auxinit(hv);
}
return
HvTOTALKEYS(hv);
}
I32 *
Perl_hv_riter_p(pTHX_ HV *hv) {
struct
xpvhv_aux *iter;
PERL_ARGS_ASSERT_HV_RITER_P;
iter = HvHasAUX(hv) ? HvAUX(hv) : hv_auxinit(hv);
return
&(iter->xhv_riter);
}
HE **
Perl_hv_eiter_p(pTHX_ HV *hv) {
struct
xpvhv_aux *iter;
PERL_ARGS_ASSERT_HV_EITER_P;
iter = HvHasAUX(hv) ? HvAUX(hv) : hv_auxinit(hv);
return
&(iter->xhv_eiter);
}
void
Perl_hv_riter_set(pTHX_ HV *hv, I32 riter) {
struct
xpvhv_aux *iter;
PERL_ARGS_ASSERT_HV_RITER_SET;
if
(HvHasAUX(hv)) {
iter = HvAUX(hv);
}
else
{
if
(riter == -1)
return
;
iter = hv_auxinit(hv);
}
iter->xhv_riter = riter;
}
void
Perl_hv_rand_set(pTHX_ HV *hv, U32 new_xhv_rand) {
struct
xpvhv_aux *iter;
PERL_ARGS_ASSERT_HV_RAND_SET;
#ifdef PERL_HASH_RANDOMIZE_KEYS
if
(HvHasAUX(hv)) {
iter = HvAUX(hv);
}
else
{
iter = hv_auxinit(hv);
}
iter->xhv_rand = new_xhv_rand;
#else
Perl_croak(aTHX_
"This Perl has not been built with support for randomized hash key traversal but something called Perl_hv_rand_set()."
);
#endif
}
void
Perl_hv_eiter_set(pTHX_ HV *hv, HE *eiter) {
struct
xpvhv_aux *iter;
PERL_ARGS_ASSERT_HV_EITER_SET;
if
(HvHasAUX(hv)) {
iter = HvAUX(hv);
}
else
{
if
(!eiter)
return
;
iter = hv_auxinit(hv);
}
iter->xhv_eiter = eiter;
}
void
Perl_hv_name_set(pTHX_ HV *hv,
const
char
*name, U32 len, U32 flags)
{
struct
xpvhv_aux *iter;
U32 hash;
HEK **spot;
PERL_ARGS_ASSERT_HV_NAME_SET;
if
(len > I32_MAX)
Perl_croak(aTHX_
"panic: hv name too long (%"
UVuf
")"
, (UV) len);
if
(HvHasAUX(hv)) {
iter = HvAUX(hv);
if
(iter->xhv_name_u.xhvnameu_name) {
if
(iter->xhv_name_count) {
if
(flags & HV_NAME_SETALL) {
HEK **
const
this_name = HvAUX(hv)->xhv_name_u.xhvnameu_names;
HEK **hekp = this_name + (
iter->xhv_name_count < 0
? -iter->xhv_name_count
: iter->xhv_name_count
);
while
(hekp-- > this_name+1)
unshare_hek_or_pvn(*hekp, 0, 0, 0);
if
(*this_name) unshare_hek_or_pvn(*this_name, 0, 0, 0);
Safefree(this_name);
spot = &iter->xhv_name_u.xhvnameu_name;
iter->xhv_name_count = 0;
}
else
{
if
(iter->xhv_name_count > 0) {
Renew(
iter->xhv_name_u.xhvnameu_names, iter->xhv_name_count + 1, HEK *
);
spot = iter->xhv_name_u.xhvnameu_names;
spot[iter->xhv_name_count] = spot[1];
spot[1] = spot[0];
iter->xhv_name_count = -(iter->xhv_name_count + 1);
}
else
if
(*(spot = iter->xhv_name_u.xhvnameu_names)) {
unshare_hek_or_pvn(*spot, 0, 0, 0);
}
}
}
else
if
(flags & HV_NAME_SETALL) {
unshare_hek_or_pvn(iter->xhv_name_u.xhvnameu_name, 0, 0, 0);
spot = &iter->xhv_name_u.xhvnameu_name;
}
else
{
HEK *
const
existing_name = iter->xhv_name_u.xhvnameu_name;
Newx(iter->xhv_name_u.xhvnameu_names, 2, HEK *);
iter->xhv_name_count = -2;
spot = iter->xhv_name_u.xhvnameu_names;
spot[1] = existing_name;
}
}
else
{ spot = &iter->xhv_name_u.xhvnameu_name; iter->xhv_name_count = 0; }
}
else
{
if
(name == 0)
return
;
iter = hv_auxinit(hv);
spot = &iter->xhv_name_u.xhvnameu_name;
}
PERL_HASH(hash, name, len);
*spot = name ? share_hek(name, flags & SVf_UTF8 ? -(I32)len : (I32)len, hash) : NULL;
}
STATIC I32
hek_eq_pvn_flags(pTHX_
const
HEK *hek,
const
char
* pv,
const
I32 pvlen,
const
U32 flags) {
if
( (HEK_UTF8(hek) ? 1 : 0) != (flags & SVf_UTF8 ? 1 : 0) ) {
if
(flags & SVf_UTF8)
return
(bytes_cmp_utf8(
(
const
U8*)HEK_KEY(hek), HEK_LEN(hek),
(
const
U8*)pv, pvlen) == 0);
else
return
(bytes_cmp_utf8(
(
const
U8*)pv, pvlen,
(
const
U8*)HEK_KEY(hek), HEK_LEN(hek)) == 0);
}
else
return
HEK_LEN(hek) == pvlen && ((HEK_KEY(hek) == pv)
|| memEQ(HEK_KEY(hek), pv, pvlen));
}
void
Perl_hv_ename_add(pTHX_ HV *hv,
const
char
*name, U32 len, U32 flags)
{
struct
xpvhv_aux *aux = HvHasAUX(hv) ? HvAUX(hv) : hv_auxinit(hv);
U32 hash;
PERL_ARGS_ASSERT_HV_ENAME_ADD;
if
(len > I32_MAX)
Perl_croak(aTHX_
"panic: hv name too long (%"
UVuf
")"
, (UV) len);
PERL_HASH(hash, name, len);
if
(aux->xhv_name_count) {
I32 count = aux->xhv_name_count;
HEK **
const
xhv_name = aux->xhv_name_u.xhvnameu_names + (count<0);
HEK **hekp = xhv_name + (count < 0 ? -count - 1 : count);
while
(hekp-- > xhv_name)
{
assert
(*hekp);
if
(
(HEK_UTF8(*hekp) || (flags & SVf_UTF8))
? hek_eq_pvn_flags(aTHX_ *hekp, name, (I32)len, flags)
: (HEK_LEN(*hekp) == (I32)len && memEQ(HEK_KEY(*hekp), name, len))
) {
if
(hekp == xhv_name && count < 0)
aux->xhv_name_count = -count;
return
;
}
}
if
(count < 0) aux->xhv_name_count--, count = -count;
else
aux->xhv_name_count++;
Renew(aux->xhv_name_u.xhvnameu_names, count + 1, HEK *);
(aux->xhv_name_u.xhvnameu_names)[count] = share_hek(name, (flags & SVf_UTF8 ? -(I32)len : (I32)len), hash);
}
else
{
HEK *existing_name = aux->xhv_name_u.xhvnameu_name;
if
(
existing_name && (
(HEK_UTF8(existing_name) || (flags & SVf_UTF8))
? hek_eq_pvn_flags(aTHX_ existing_name, name, (I32)len, flags)
: (HEK_LEN(existing_name) == (I32)len && memEQ(HEK_KEY(existing_name), name, len))
)
)
return
;
Newx(aux->xhv_name_u.xhvnameu_names, 2, HEK *);
aux->xhv_name_count = existing_name ? 2 : -2;
*aux->xhv_name_u.xhvnameu_names = existing_name;
(aux->xhv_name_u.xhvnameu_names)[1] = share_hek(name, (flags & SVf_UTF8 ? -(I32)len : (I32)len), hash);
}
}
void
Perl_hv_ename_delete(pTHX_ HV *hv,
const
char
*name, U32 len, U32 flags)
{
struct
xpvhv_aux *aux;
PERL_ARGS_ASSERT_HV_ENAME_DELETE;
if
(len > I32_MAX)
Perl_croak(aTHX_
"panic: hv name too long (%"
UVuf
")"
, (UV) len);
if
(!HvHasAUX(hv))
return
;
aux = HvAUX(hv);
if
(!aux->xhv_name_u.xhvnameu_name)
return
;
if
(aux->xhv_name_count) {
HEK **
const
namep = aux->xhv_name_u.xhvnameu_names;
I32
const
count = aux->xhv_name_count;
HEK **victim = namep + (count < 0 ? -count : count);
while
(victim-- > namep + 1)
if
(
(HEK_UTF8(*victim) || (flags & SVf_UTF8))
? hek_eq_pvn_flags(aTHX_ *victim, name, (I32)len, flags)
: (HEK_LEN(*victim) == (I32)len && memEQ(HEK_KEY(*victim), name, len))
) {
unshare_hek_or_pvn(*victim, 0, 0, 0);
if
(count < 0) ++aux->xhv_name_count;
else
--aux->xhv_name_count;
if
(
(aux->xhv_name_count == 1 || aux->xhv_name_count == -1)
&& !*namep
) {
Safefree(namep);
aux->xhv_name_u.xhvnameu_names = NULL;
aux->xhv_name_count = 0;
}
else
{
*victim = *(namep + (count < 0 ? -count : count) - 1);
}
return
;
}
if
(
count > 0 && ((HEK_UTF8(*namep) || (flags & SVf_UTF8))
? hek_eq_pvn_flags(aTHX_ *namep, name, (I32)len, flags)
: (HEK_LEN(*namep) == (I32)len && memEQ(HEK_KEY(*namep), name, len))
)
) {
aux->xhv_name_count = -count;
}
}
else
if
(
(HEK_UTF8(aux->xhv_name_u.xhvnameu_name) || (flags & SVf_UTF8))
? hek_eq_pvn_flags(aTHX_ aux->xhv_name_u.xhvnameu_name, name, (I32)len, flags)
: (HEK_LEN(aux->xhv_name_u.xhvnameu_name) == (I32)len &&
memEQ(HEK_KEY(aux->xhv_name_u.xhvnameu_name), name, len))
) {
HEK *
const
namehek = aux->xhv_name_u.xhvnameu_name;
Newx(aux->xhv_name_u.xhvnameu_names, 1, HEK *);
*aux->xhv_name_u.xhvnameu_names = namehek;
aux->xhv_name_count = -1;
}
}
AV **
Perl_hv_backreferences_p(pTHX_ HV *hv) {
PERL_ARGS_ASSERT_HV_BACKREFERENCES_P;
{
struct
xpvhv_aux *
const
iter = HvHasAUX(hv) ? HvAUX(hv) : hv_auxinit(hv);
return
&(iter->xhv_backreferences);
}
}
void
Perl_hv_kill_backrefs(pTHX_ HV *hv) {
AV *av;
PERL_ARGS_ASSERT_HV_KILL_BACKREFS;
if
(!HvHasAUX(hv))
return
;
av = HvAUX(hv)->xhv_backreferences;
if
(av) {
HvAUX(hv)->xhv_backreferences = 0;
Perl_sv_kill_backrefs(aTHX_ MUTABLE_SV(hv), av);
if
(SvTYPE(av) == SVt_PVAV)
SvREFCNT_dec_NN(av);
}
}
HE *
Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
{
HE *entry;
HE *oldentry;
MAGIC* mg;
struct
xpvhv_aux *iter;
PERL_ARGS_ASSERT_HV_ITERNEXT_FLAGS;
if
(!HvHasAUX(hv)) {
hv_iterinit(hv);
}
else
if
(!HvARRAY(hv)) {
char
*array;
Newxz(array, PERL_HV_ARRAY_ALLOC_BYTES(HvMAX(hv) + 1),
char
);
HvARRAY(hv) = (HE**)array;
}
iter = HvAUX(hv);
oldentry = entry = iter->xhv_eiter;
if
(SvMAGICAL(hv) && SvRMAGICAL(hv)) {
if
( ( mg = mg_find((
const
SV *)hv, PERL_MAGIC_tied) ) ) {
SV *
const
key = sv_newmortal();
if
(entry) {
sv_setsv(key, HeSVKEY_force(entry));
SvREFCNT_dec(HeSVKEY(entry));
HeSVKEY_set(entry, NULL);
}
else
{
char
*k;
HEK *hek;
iter->xhv_eiter = entry = new_HE();
HvLAZYDEL_on(hv);
Zero(entry, 1, HE);
Newxz(k, HEK_BASESIZE +
sizeof
(
const
SV *),
char
);
hek = (HEK*)k;
HeKEY_hek(entry) = hek;
HeKLEN(entry) = HEf_SVKEY;
}
magic_nextpack(MUTABLE_SV(hv),mg,key);
if
(SvOK(key)) {
HeSVKEY_set(entry, SvREFCNT_inc_simple_NN(key));
return
entry;
}
SvREFCNT_dec(HeVAL(entry));
Safefree(HeKEY_hek(entry));
del_HE(entry);
iter->xhv_eiter = NULL;
HvLAZYDEL_off(hv);
return
NULL;
}
}
#if defined(DYNAMIC_ENV_FETCH) && defined(VMS) /* set up %ENV for iteration */
if
(!entry && SvRMAGICAL((
const
SV *)hv)
&& mg_find((
const
SV *)hv, PERL_MAGIC_env)) {
prime_env_iter();
}
#endif
assert
(HvARRAY(hv));
if
(entry)
{
entry = HeNEXT(entry);
if
(!(flags & HV_ITERNEXT_WANTPLACEHOLDERS)) {
while
(entry && HeVAL(entry) == &PL_sv_placeholder) {
entry = HeNEXT(entry);
}
}
}
#ifdef PERL_HASH_RANDOMIZE_KEYS
if
(iter->xhv_last_rand != iter->xhv_rand) {
if
(iter->xhv_riter != -1) {
Perl_ck_warner_d(aTHX_ packWARN(WARN_INTERNAL),
"Use of each() on hash after insertion without resetting hash iterator results in undefined behavior"
pTHX__FORMAT
pTHX__VALUE);
}
iter->xhv_last_rand = iter->xhv_rand;
}
#endif
if
((flags & HV_ITERNEXT_WANTPLACEHOLDERS)
? HvTOTALKEYS(hv) : HvUSEDKEYS(hv)) {
STRLEN max = HvMAX(hv);
while
(!entry) {
iter->xhv_riter++;
if
(iter->xhv_riter > (I32)max
) {
iter->xhv_riter = -1;
#ifdef PERL_HASH_RANDOMIZE_KEYS
iter->xhv_last_rand = iter->xhv_rand;
#endif
break
;
}
entry = (HvARRAY(hv))[ PERL_HASH_ITER_BUCKET(iter) & max ];
if
(!(flags & HV_ITERNEXT_WANTPLACEHOLDERS)) {
while
(entry && HeVAL(entry) == &PL_sv_placeholder)
entry = HeNEXT(entry);
}
}
}
else
{
iter->xhv_riter = -1;
#ifdef PERL_HASH_RANDOMIZE_KEYS
iter->xhv_last_rand = iter->xhv_rand;
#endif
}
if
(oldentry && HvLAZYDEL(hv)) {
HvLAZYDEL_off(hv);
hv_free_ent(NULL, oldentry);
}
iter->xhv_eiter = entry;
return
entry;
}
char
*
Perl_hv_iterkey(pTHX_ HE *entry, I32 *retlen)
{
PERL_ARGS_ASSERT_HV_ITERKEY;
if
(HeKLEN(entry) == HEf_SVKEY) {
STRLEN len;
char
*
const
p = SvPV(HeKEY_sv(entry), len);
*retlen = len;
return
p;
}
else
{
*retlen = HeKLEN(entry);
return
HeKEY(entry);
}
}
SV *
Perl_hv_iterkeysv(pTHX_ HE *entry)
{
PERL_ARGS_ASSERT_HV_ITERKEYSV;
return
newSVhek_mortal(HeKEY_hek(entry));
}
SV *
Perl_hv_iterval(pTHX_ HV *hv, HE *entry)
{
PERL_ARGS_ASSERT_HV_ITERVAL;
if
(SvRMAGICAL(hv)) {
if
(mg_find((
const
SV *)hv, PERL_MAGIC_tied)) {
SV*
const
sv = sv_newmortal();
if
(HeKLEN(entry) == HEf_SVKEY)
mg_copy(MUTABLE_SV(hv), sv, (
char
*)HeKEY_sv(entry), HEf_SVKEY);
else
mg_copy(MUTABLE_SV(hv), sv, HeKEY(entry), HeKLEN(entry));
return
sv;
}
}
return
HeVAL(entry);
}
SV *
Perl_hv_iternextsv(pTHX_ HV *hv,
char
**key, I32 *retlen)
{
HE *
const
he = hv_iternext_flags(hv, 0);
PERL_ARGS_ASSERT_HV_ITERNEXTSV;
if
(!he)
return
NULL;
*key = hv_iterkey(he, retlen);
return
hv_iterval(hv, he);
}
void
Perl_unsharepvn(pTHX_
const
char
*str, I32 len, U32 hash)
{
unshare_hek_or_pvn (NULL, str, len, hash);
}
void
Perl_unshare_hek(pTHX_ HEK *hek)
{
assert
(hek);
unshare_hek_or_pvn(hek, NULL, 0, 0);
}
STATIC
void
S_unshare_hek_or_pvn(pTHX_
const
HEK *hek,
const
char
*str, I32 len, U32 hash)
{
HE *entry;
HE **oentry;
bool
is_utf8 = FALSE;
int
k_flags = 0;
const
char
*
const
save = str;
struct
shared_he *he = NULL;
if
(hek) {
assert
((HEK_FLAGS(hek) & HVhek_NOTSHARED) == 0);
he = (
struct
shared_he *)(((
char
*)hek)
- STRUCT_OFFSET(
struct
shared_he,
shared_he_hek));
assert
(he->shared_he_he.hent_hek == hek);
if
(he->shared_he_he.he_valu.hent_refcount - 1) {
--he->shared_he_he.he_valu.hent_refcount;
return
;
}
hash = HEK_HASH(hek);
}
else
if
(len < 0) {
STRLEN tmplen = -len;
is_utf8 = TRUE;
str = (
char
*)bytes_from_utf8((U8*)str, &tmplen, &is_utf8);
len = tmplen;
if
(is_utf8)
k_flags = HVhek_UTF8;
if
(str != save)
k_flags |= HVhek_WASUTF8 | HVhek_FREEKEY;
}
oentry = &(HvARRAY(PL_strtab))[hash & (I32) HvMAX(PL_strtab)];
if
(he) {
const
HE *
const
he_he = &(he->shared_he_he);
for
(entry = *oentry; entry; oentry = &HeNEXT(entry), entry = *oentry) {
if
(entry == he_he)
break
;
}
}
else
{
const
U8 flags_masked = k_flags & HVhek_STORAGE_MASK;
for
(entry = *oentry; entry; oentry = &HeNEXT(entry), entry = *oentry) {
if
(HeHASH(entry) != hash)
continue
;
if
(HeKLEN(entry) != len)
continue
;
if
(HeKEY(entry) != str && memNE(HeKEY(entry),str,len))
continue
;
if
(HeKFLAGS(entry) != flags_masked)
continue
;
break
;
}
}
if
(entry) {
if
(--entry->he_valu.hent_refcount == 0) {
*oentry = HeNEXT(entry);
Safefree(entry);
HvTOTALKEYS(PL_strtab)--;
}
}
if
(!entry)
Perl_ck_warner_d(aTHX_ packWARN(WARN_INTERNAL),
"Attempt to free nonexistent shared string '%s'%s"
pTHX__FORMAT,
hek ? HEK_KEY(hek) : str,
((k_flags & HVhek_UTF8) ?
" (utf8)"
:
""
) pTHX__VALUE);
if
(k_flags & HVhek_FREEKEY)
Safefree(str);
}
HEK *
Perl_share_hek(pTHX_
const
char
*str, SSize_t len, U32 hash)
{
bool
is_utf8 = FALSE;
int
flags = 0;
const
char
*
const
save = str;
PERL_ARGS_ASSERT_SHARE_HEK;
if
(len < 0) {
STRLEN tmplen = -len;
is_utf8 = TRUE;
str = (
char
*)bytes_from_utf8((U8*)str, &tmplen, &is_utf8);
len = tmplen;
if
(is_utf8)
flags = HVhek_UTF8;
if
(str != save) {
PERL_HASH(hash, str, len);
flags |= HVhek_WASUTF8 | HVhek_FREEKEY;
}
}
return
share_hek_flags (str, len, hash, flags);
}
STATIC HEK *
S_share_hek_flags(pTHX_
const
char
*str, STRLEN len, U32 hash,
int
flags)
{
HE *entry;
const
U8 flags_masked = flags & HVhek_STORAGE_MASK;
const
U32 hindex = hash & (I32) HvMAX(PL_strtab);
PERL_ARGS_ASSERT_SHARE_HEK_FLAGS;
assert
(!(flags & HVhek_NOTSHARED));
if
(UNLIKELY(len > (STRLEN) I32_MAX)) {
Perl_croak_nocontext(
"Sorry, hash keys must be smaller than 2**31 bytes"
);
}
entry = (HvARRAY(PL_strtab))[hindex];
for
(;entry; entry = HeNEXT(entry)) {
if
(HeHASH(entry) != hash)
continue
;
if
(HeKLEN(entry) != (SSize_t) len)
continue
;
if
(HeKEY(entry) != str && memNE(HeKEY(entry),str,len))
continue
;
if
(HeKFLAGS(entry) != flags_masked)
continue
;
break
;
}
if
(!entry) {
struct
shared_he *new_entry;
HEK *hek;
char
*k;
HE **
const
head = &HvARRAY(PL_strtab)[hindex];
HE *
const
next = *head;
XPVHV *
const
xhv = (XPVHV*)SvANY(PL_strtab);
Newx(k, STRUCT_OFFSET(
struct
shared_he,
shared_he_hek.hek_key[0]) + len + 2,
char
);
new_entry = (
struct
shared_he *)k;
entry = &(new_entry->shared_he_he);
hek = &(new_entry->shared_he_hek);
Copy(str, HEK_KEY(hek), len,
char
);
HEK_KEY(hek)[len] = 0;
HEK_LEN(hek) = len;
HEK_HASH(hek) = hash;
HEK_FLAGS(hek) = (unsigned
char
)flags_masked;
HeKEY_hek(entry) = hek;
entry->he_valu.hent_refcount = 0;
HeNEXT(entry) = next;
*head = entry;
xhv->xhv_keys++;
if
(!next) {
}
else
if
( DO_HSPLIT(xhv) ) {
const
STRLEN oldsize = xhv->xhv_max + 1;
hsplit(PL_strtab, oldsize, oldsize * 2);
}
}
++entry->he_valu.hent_refcount;
if
(flags & HVhek_FREEKEY)
Safefree(str);
return
HeKEY_hek(entry);
}
SSize_t *
Perl_hv_placeholders_p(pTHX_ HV *hv)
{
MAGIC *mg = mg_find((
const
SV *)hv, PERL_MAGIC_rhash);
PERL_ARGS_ASSERT_HV_PLACEHOLDERS_P;
if
(!mg) {
mg = sv_magicext(MUTABLE_SV(hv), 0, PERL_MAGIC_rhash, 0, 0, 0);
if
(!mg) {
Perl_die(aTHX_
"panic: hv_placeholders_p"
);
}
}
return
&(mg->mg_len);
}
I32
Perl_hv_placeholders_get(pTHX_
const
HV *hv)
{
MAGIC *
const
mg = mg_find((
const
SV *)hv, PERL_MAGIC_rhash);
PERL_ARGS_ASSERT_HV_PLACEHOLDERS_GET;
PERL_UNUSED_CONTEXT;
return
mg ? mg->mg_len : 0;
}
void
Perl_hv_placeholders_set(pTHX_ HV *hv, I32 ph)
{
MAGIC *
const
mg = mg_find((
const
SV *)hv, PERL_MAGIC_rhash);
PERL_ARGS_ASSERT_HV_PLACEHOLDERS_SET;
if
(mg) {
mg->mg_len = ph;
}
else
if
(ph) {
if
(!sv_magicext(MUTABLE_SV(hv), 0, PERL_MAGIC_rhash, 0, 0, ph))
Perl_die(aTHX_
"panic: hv_placeholders_set"
);
}
}
STATIC SV *
S_refcounted_he_value(pTHX_
const
struct
refcounted_he *he)
{
SV *value;
PERL_ARGS_ASSERT_REFCOUNTED_HE_VALUE;
switch
(he->refcounted_he_data[0] & HVrhek_typemask) {
case
HVrhek_undef:
value = newSV_type(SVt_NULL);
break
;
case
HVrhek_delete:
value = &PL_sv_placeholder;
break
;
case
HVrhek_IV:
value = newSViv(he->refcounted_he_val.refcounted_he_u_iv);
break
;
case
HVrhek_UV:
value = newSVuv(he->refcounted_he_val.refcounted_he_u_uv);
break
;
case
HVrhek_PV:
case
HVrhek_PV_UTF8:
value = newSV_type(SVt_PV);
SvPV_set(value, (
char
*) he->refcounted_he_data + 1);
SvCUR_set(value, he->refcounted_he_val.refcounted_he_u_len);
SvLEN_set(value, 0);
SvPOK_on(value);
SvREADONLY_on(value);
if
((he->refcounted_he_data[0] & HVrhek_typemask) == HVrhek_PV_UTF8)
SvUTF8_on(value);
break
;
default
:
Perl_croak(aTHX_
"panic: refcounted_he_value bad flags %"
UVxf,
(UV)he->refcounted_he_data[0]);
}
return
value;
}
HV *
Perl_refcounted_he_chain_2hv(pTHX_
const
struct
refcounted_he *chain, U32 flags)
{
HV *hv;
U32 placeholders, max;
if
(flags)
Perl_croak(aTHX_
"panic: refcounted_he_chain_2hv bad flags %"
UVxf,
(UV)flags);
hv = newHV();
#ifdef NODEFAULT_SHAREKEYS
HvSHAREKEYS_on(hv);
#endif
max = HvMAX(hv);
if
(!HvARRAY(hv)) {
char
*array;
Newxz(array, PERL_HV_ARRAY_ALLOC_BYTES(max + 1),
char
);
HvARRAY(hv) = (HE**)array;
}
placeholders = 0;
while
(chain) {
#ifdef USE_ITHREADS
U32 hash = chain->refcounted_he_hash;
#else
U32 hash = HEK_HASH(chain->refcounted_he_hek);
#endif
HE **oentry = &((HvARRAY(hv))[hash & max]);
HE *entry = *oentry;
SV *value;
for
(; entry; entry = HeNEXT(entry)) {
if
(HeHASH(entry) == hash) {
#ifdef USE_ITHREADS
const
STRLEN klen = HeKLEN(entry);
const
char
*
const
key = HeKEY(entry);
if
(klen == chain->refcounted_he_keylen
&& (cBOOL(HeKUTF8(entry))
== cBOOL((chain->refcounted_he_data[0] & HVhek_UTF8)))
&& memEQ(key, REF_HE_KEY(chain), klen))
goto
next_please;
#else
if
(HeKEY_hek(entry) == chain->refcounted_he_hek)
goto
next_please;
if
(HeKLEN(entry) == HEK_LEN(chain->refcounted_he_hek)
&& HeKUTF8(entry) == HEK_UTF8(chain->refcounted_he_hek)
&& memEQ(HeKEY(entry), HEK_KEY(chain->refcounted_he_hek),
HeKLEN(entry)))
goto
next_please;
#endif
}
}
assert
(!entry);
entry = new_HE();
#ifdef USE_ITHREADS
HeKEY_hek(entry)
= share_hek_flags(REF_HE_KEY(chain),
chain->refcounted_he_keylen,
chain->refcounted_he_hash,
(chain->refcounted_he_data[0]
& (HVhek_UTF8|HVhek_WASUTF8)));
#else
HeKEY_hek(entry) = share_hek_hek(chain->refcounted_he_hek);
#endif
value = refcounted_he_value(chain);
if
(value == &PL_sv_placeholder)
placeholders++;
HeVAL(entry) = value;
HeNEXT(entry) = *oentry;
*oentry = entry;
HvTOTALKEYS(hv)++;
next_please:
chain = chain->refcounted_he_next;
}
if
(placeholders) {
clear_placeholders(hv, placeholders);
}
HvHASKFLAGS_on(hv);
DEBUG_A(Perl_hv_assert(aTHX_ hv));
return
hv;
}
SV *
Perl_refcounted_he_fetch_pvn(pTHX_
const
struct
refcounted_he *chain,
const
char
*keypv, STRLEN keylen, U32 hash, U32 flags)
{
U8 utf8_flag;
PERL_ARGS_ASSERT_REFCOUNTED_HE_FETCH_PVN;
if
(flags & ~(REFCOUNTED_HE_KEY_UTF8|REFCOUNTED_HE_EXISTS))
Perl_croak(aTHX_
"panic: refcounted_he_fetch_pvn bad flags %"
UVxf,
(UV)flags);
if
(!chain)
goto
ret;
if
(flags & REFCOUNTED_HE_KEY_UTF8) {
const
char
*keyend = keypv + keylen, *p;
STRLEN nonascii_count = 0;
for
(p = keypv; p != keyend; p++) {
if
(! UTF8_IS_INVARIANT(*p)) {
if
(! UTF8_IS_NEXT_CHAR_DOWNGRADEABLE(p, keyend)) {
goto
canonicalised_key;
}
nonascii_count++;
p++;
}
}
if
(nonascii_count) {
char
*q;
const
char
*p = keypv, *keyend = keypv + keylen;
keylen -= nonascii_count;
Newx(q, keylen,
char
);
SAVEFREEPV(q);
keypv = q;
for
(; p != keyend; p++, q++) {
U8 c = (U8)*p;
if
(UTF8_IS_INVARIANT(c)) {
*q = (
char
) c;
}
else
{
p++;
*q = (
char
) EIGHT_BIT_UTF8_TO_NATIVE(c, *p);
}
}
}
flags &= ~REFCOUNTED_HE_KEY_UTF8;
canonicalised_key: ;
}
utf8_flag = (flags & REFCOUNTED_HE_KEY_UTF8) ? HVhek_UTF8 : 0;
if
(!hash)
PERL_HASH(hash, keypv, keylen);
for
(; chain; chain = chain->refcounted_he_next) {
if
(
#ifdef USE_ITHREADS
hash == chain->refcounted_he_hash &&
keylen == chain->refcounted_he_keylen &&
memEQ(REF_HE_KEY(chain), keypv, keylen) &&
utf8_flag == (chain->refcounted_he_data[0] & HVhek_UTF8)
#else
hash == HEK_HASH(chain->refcounted_he_hek) &&
keylen == (STRLEN)HEK_LEN(chain->refcounted_he_hek) &&
memEQ(HEK_KEY(chain->refcounted_he_hek), keypv, keylen) &&
utf8_flag == (HEK_FLAGS(chain->refcounted_he_hek) & HVhek_UTF8)
#endif
) {
if
(flags & REFCOUNTED_HE_EXISTS)
return
(chain->refcounted_he_data[0] & HVrhek_typemask)
== HVrhek_delete
? NULL : &PL_sv_yes;
return
sv_2mortal(refcounted_he_value(chain));
}
}
ret:
return
flags & REFCOUNTED_HE_EXISTS ? NULL : &PL_sv_placeholder;
}
SV *
Perl_refcounted_he_fetch_pv(pTHX_
const
struct
refcounted_he *chain,
const
char
*key, U32 hash, U32 flags)
{
PERL_ARGS_ASSERT_REFCOUNTED_HE_FETCH_PV;
return
refcounted_he_fetch_pvn(chain, key,
strlen
(key), hash, flags);
}
SV *
Perl_refcounted_he_fetch_sv(pTHX_
const
struct
refcounted_he *chain,
SV *key, U32 hash, U32 flags)
{
const
char
*keypv;
STRLEN keylen;
PERL_ARGS_ASSERT_REFCOUNTED_HE_FETCH_SV;
if
(flags & REFCOUNTED_HE_KEY_UTF8)
Perl_croak(aTHX_
"panic: refcounted_he_fetch_sv bad flags %"
UVxf,
(UV)flags);
keypv = SvPV_const(key, keylen);
if
(SvUTF8(key))
flags |= REFCOUNTED_HE_KEY_UTF8;
if
(!hash && SvIsCOW_shared_hash(key))
hash = SvSHARED_HASH(key);
return
refcounted_he_fetch_pvn(chain, keypv, keylen, hash, flags);
}
struct
refcounted_he *
Perl_refcounted_he_new_pvn(pTHX_
struct
refcounted_he *parent,
const
char
*keypv, STRLEN keylen, U32 hash, SV *value, U32 flags)
{
STRLEN value_len = 0;
const
char
*value_p = NULL;
bool
is_pv;
char
value_type;
char
hekflags;
STRLEN key_offset = 1;
struct
refcounted_he *he;
PERL_ARGS_ASSERT_REFCOUNTED_HE_NEW_PVN;
if
(!value || value == &PL_sv_placeholder) {
value_type = HVrhek_delete;
}
else
if
(SvPOK(value)) {
value_type = HVrhek_PV;
}
else
if
(SvIOK(value)) {
value_type = SvUOK((
const
SV *)value) ? HVrhek_UV : HVrhek_IV;
}
else
if
(!SvOK(value)) {
value_type = HVrhek_undef;
}
else
{
value_type = HVrhek_PV;
}
is_pv = value_type == HVrhek_PV;
if
(is_pv) {
value_p = SvPV_const(value, value_len);
if
(SvUTF8(value))
value_type = HVrhek_PV_UTF8;
key_offset = value_len + 2;
}
hekflags = value_type;
if
(flags & REFCOUNTED_HE_KEY_UTF8) {
const
char
*keyend = keypv + keylen, *p;
STRLEN nonascii_count = 0;
for
(p = keypv; p != keyend; p++) {
if
(! UTF8_IS_INVARIANT(*p)) {
if
(! UTF8_IS_NEXT_CHAR_DOWNGRADEABLE(p, keyend)) {
goto
canonicalised_key;
}
nonascii_count++;
p++;
}
}
if
(nonascii_count) {
char
*q;
const
char
*p = keypv, *keyend = keypv + keylen;
keylen -= nonascii_count;
Newx(q, keylen,
char
);
SAVEFREEPV(q);
keypv = q;
for
(; p != keyend; p++, q++) {
U8 c = (U8)*p;
if
(UTF8_IS_INVARIANT(c)) {
*q = (
char
) c;
}
else
{
p++;
*q = (
char
) EIGHT_BIT_UTF8_TO_NATIVE(c, *p);
}
}
}
flags &= ~REFCOUNTED_HE_KEY_UTF8;
canonicalised_key: ;
}
if
(flags & REFCOUNTED_HE_KEY_UTF8)
hekflags |= HVhek_UTF8;
if
(!hash)
PERL_HASH(hash, keypv, keylen);
#ifdef USE_ITHREADS
he = (
struct
refcounted_he*)
PerlMemShared_malloc(
sizeof
(
struct
refcounted_he) - 1
+ keylen
+ key_offset);
#else
he = (
struct
refcounted_he*)
PerlMemShared_malloc(
sizeof
(
struct
refcounted_he) - 1
+ key_offset);
#endif
he->refcounted_he_next = parent;
if
(is_pv) {
Copy(value_p, he->refcounted_he_data + 1, value_len + 1,
char
);
he->refcounted_he_val.refcounted_he_u_len = value_len;
}
else
if
(value_type == HVrhek_IV) {
he->refcounted_he_val.refcounted_he_u_iv = SvIVX(value);
}
else
if
(value_type == HVrhek_UV) {
he->refcounted_he_val.refcounted_he_u_uv = SvUVX(value);
}
#ifdef USE_ITHREADS
he->refcounted_he_hash = hash;
he->refcounted_he_keylen = keylen;
Copy(keypv, he->refcounted_he_data + key_offset, keylen,
char
);
#else
he->refcounted_he_hek = share_hek_flags(keypv, keylen, hash, hekflags);
#endif
he->refcounted_he_data[0] = hekflags;
he->refcounted_he_refcnt = 1;
return
he;
}
struct
refcounted_he *
Perl_refcounted_he_new_pv(pTHX_
struct
refcounted_he *parent,
const
char
*key, U32 hash, SV *value, U32 flags)
{
PERL_ARGS_ASSERT_REFCOUNTED_HE_NEW_PV;
return
refcounted_he_new_pvn(parent, key,
strlen
(key), hash, value, flags);
}
struct
refcounted_he *
Perl_refcounted_he_new_sv(pTHX_
struct
refcounted_he *parent,
SV *key, U32 hash, SV *value, U32 flags)
{
const
char
*keypv;
STRLEN keylen;
PERL_ARGS_ASSERT_REFCOUNTED_HE_NEW_SV;
if
(flags & REFCOUNTED_HE_KEY_UTF8)
Perl_croak(aTHX_
"panic: refcounted_he_new_sv bad flags %"
UVxf,
(UV)flags);
keypv = SvPV_const(key, keylen);
if
(SvUTF8(key))
flags |= REFCOUNTED_HE_KEY_UTF8;
if
(!hash && SvIsCOW_shared_hash(key))
hash = SvSHARED_HASH(key);
return
refcounted_he_new_pvn(parent, keypv, keylen, hash, value, flags);
}
void
Perl_refcounted_he_free(pTHX_
struct
refcounted_he *he) {
PERL_UNUSED_CONTEXT;
while
(he) {
struct
refcounted_he *copy;
U32 new_count;
HINTS_REFCNT_LOCK;
new_count = --he->refcounted_he_refcnt;
HINTS_REFCNT_UNLOCK;
if
(new_count) {
return
;
}
#ifndef USE_ITHREADS
unshare_hek_or_pvn (he->refcounted_he_hek, 0, 0, 0);
#endif
copy = he;
he = he->refcounted_he_next;
PerlMemShared_free(copy);
}
}
struct
refcounted_he *
Perl_refcounted_he_inc(pTHX_
struct
refcounted_he *he)
{
PERL_UNUSED_CONTEXT;
if
(he) {
HINTS_REFCNT_LOCK;
he->refcounted_he_refcnt++;
HINTS_REFCNT_UNLOCK;
}
return
he;
}
const
char
*
Perl_cop_fetch_label(pTHX_ COP *
const
cop, STRLEN *len, U32 *flags) {
struct
refcounted_he *
const
chain = cop->cop_hints_hash;
PERL_ARGS_ASSERT_COP_FETCH_LABEL;
PERL_UNUSED_CONTEXT;
if
(!chain)
return
NULL;
#ifdef USE_ITHREADS
if
(chain->refcounted_he_keylen != 1)
return
NULL;
if
(*REF_HE_KEY(chain) !=
':'
)
return
NULL;
#else
if
((STRLEN)HEK_LEN(chain->refcounted_he_hek) != 1)
return
NULL;
if
(*HEK_KEY(chain->refcounted_he_hek) !=
':'
)
return
NULL;
#endif
if
((chain->refcounted_he_data[0] & HVrhek_typemask) != HVrhek_PV
&& (chain->refcounted_he_data[0] & HVrhek_typemask) != HVrhek_PV_UTF8)
return
NULL;
if
(len)
*len = chain->refcounted_he_val.refcounted_he_u_len;
if
(flags) {
*flags = ((chain->refcounted_he_data[0] & HVrhek_typemask)
== HVrhek_PV_UTF8) ? SVf_UTF8 : 0;
}
return
chain->refcounted_he_data + 1;
}
void
Perl_cop_store_label(pTHX_ COP *
const
cop,
const
char
*label, STRLEN len,
U32 flags)
{
SV *labelsv;
PERL_ARGS_ASSERT_COP_STORE_LABEL;
if
(flags & ~(SVf_UTF8))
Perl_croak(aTHX_
"panic: cop_store_label illegal flag bits 0x%"
UVxf,
(UV)flags);
labelsv = newSVpvn_flags(label, len, SVs_TEMP);
if
(flags & SVf_UTF8)
SvUTF8_on(labelsv);
cop->cop_hints_hash
= refcounted_he_new_pvs(cop->cop_hints_hash,
":"
, labelsv, 0);
}
#ifdef DEBUGGING
void
Perl_hv_assert(pTHX_ HV *hv)
{
HE* entry;
int
withflags = 0;
int
placeholders = 0;
int
real = 0;
int
bad = 0;
const
I32 riter = HvRITER_get(hv);
HE *eiter = HvEITER_get(hv);
PERL_ARGS_ASSERT_HV_ASSERT;
(
void
)hv_iterinit(hv);
while
((entry = hv_iternext_flags(hv, HV_ITERNEXT_WANTPLACEHOLDERS))) {
if
(HeVAL(entry) == &PL_sv_placeholder)
placeholders++;
else
real++;
if
(HeSVKEY(entry)) {
NOOP;
}
else
if
(HeKUTF8(entry)) {
withflags++;
if
(HeKWASUTF8(entry)) {
PerlIO_printf(Perl_debug_log,
"hash key has both WASUTF8 and UTF8: '%.*s'\n"
,
(
int
) HeKLEN(entry), HeKEY(entry));
bad = 1;
}
}
else
if
(HeKWASUTF8(entry))
withflags++;
}
if
(!SvTIED_mg((
const
SV *)hv, PERL_MAGIC_tied)) {
static
const
char
bad_count[] =
"Count %d %s(s), but hash reports %d\n"
;
const
int
nhashkeys = HvUSEDKEYS(hv);
const
int
nhashplaceholders = HvPLACEHOLDERS_get(hv);
if
(nhashkeys != real) {
PerlIO_printf(Perl_debug_log, bad_count, real,
"keys"
, nhashkeys );
bad = 1;
}
if
(nhashplaceholders != placeholders) {
PerlIO_printf(Perl_debug_log, bad_count, placeholders,
"placeholder"
, nhashplaceholders );
bad = 1;
}
}
if
(withflags && ! HvHASKFLAGS(hv)) {
PerlIO_printf(Perl_debug_log,
"Hash has HASKFLAGS off but I count %d key(s) with flags\n"
,
withflags);
bad = 1;
}
if
(bad) {
sv_dump(MUTABLE_SV(hv));
}
HvRITER_set(hv, riter);
HvEITER_set(hv, eiter);
}
#endif