/* hv.c
*
* Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- * 2000, 2001, 2002, 2003, by Larry Wall and others
+ * 2000, 2001, 2002, 2003, 2004, 2005, by Larry Wall and others
*
* You may distribute under the terms of either the GNU General Public
* License or the Artistic License, as specified in the README file.
/*
=head1 Hash Manipulation Functions
+
+A HV structure represents a Perl hash. It consists mainly of an array
+of pointers, each of which points to a linked list of HE structures. The
+array is indexed by the hash function of the key, so each linked list
+represents all the hash entries with the same hash value. Each HE contains
+a pointer to the actual value, plus a pointer to a HEK structure which
+holds the key and hash value.
+
+=cut
+
*/
#include "EXTERN.h"
#define HV_MAX_LENGTH_BEFORE_SPLIT 14
+STATIC void
+S_more_he(pTHX)
+{
+ HE* he;
+ HE* heend;
+ New(54, he, PERL_ARENA_SIZE/sizeof(HE), HE);
+ HeNEXT(he) = PL_he_arenaroot;
+ PL_he_arenaroot = he;
+
+ heend = &he[PERL_ARENA_SIZE / sizeof(HE) - 1];
+ PL_he_root = ++he;
+ while (he < heend) {
+ HeNEXT(he) = (HE*)(he + 1);
+ he++;
+ }
+ HeNEXT(he) = 0;
+}
+
STATIC HE*
S_new_he(pTHX)
{
HE* he;
LOCK_SV_MUTEX;
if (!PL_he_root)
- more_he();
+ S_more_he(aTHX);
he = PL_he_root;
PL_he_root = HeNEXT(he);
UNLOCK_SV_MUTEX;
UNLOCK_SV_MUTEX;
}
-STATIC void
-S_more_he(pTHX)
-{
- register HE* he;
- register HE* heend;
- XPV *ptr;
- New(54, ptr, 1008/sizeof(XPV), XPV);
- ptr->xpv_pv = (char*)PL_he_arenaroot;
- PL_he_arenaroot = ptr;
-
- he = (HE*)ptr;
- heend = &he[1008 / sizeof(HE) - 1];
- PL_he_root = ++he;
- while (he < heend) {
- HeNEXT(he) = (HE*)(he + 1);
- he++;
- }
- HeNEXT(he) = 0;
-}
-
#ifdef PURIFY
#define new_HE() (HE*)safemalloc(sizeof(HE))
STATIC HEK *
S_save_hek_flags(pTHX_ const char *str, I32 len, U32 hash, int flags)
{
+ const int flags_masked = flags & HVhek_MASK;
char *k;
register HEK *hek;
HEK_KEY(hek)[len] = 0;
HEK_LEN(hek) = len;
HEK_HASH(hek) = hash;
- HEK_FLAGS(hek) = (unsigned char)flags;
+ HEK_FLAGS(hek) = (unsigned char)flags_masked;
+
+ if (flags & HVhek_FREEKEY)
+ Safefree(str);
return hek;
}
S_hv_notallowed(pTHX_ int flags, const char *key, I32 klen,
const char *msg)
{
- SV *sv = sv_newmortal(), *esv = sv_newmortal();
+ SV *sv = sv_newmortal();
if (!(flags & HVhek_FREEKEY)) {
sv_setpvn(sv, key, klen);
}
if (flags & HVhek_UTF8) {
SvUTF8_on(sv);
}
- Perl_sv_setpvf(aTHX_ esv, "Attempt to %s a restricted hash", msg);
- Perl_croak(aTHX_ SvPVX(esv), sv);
+ Perl_croak(aTHX_ msg, sv);
}
/* (klen == HEf_SVKEY) is special for MAGICAL hv entries, meaning key slot
flags = 0;
}
hek = hv_fetch_common (hv, NULL, key, klen, flags,
- (HV_FETCH_ISSTORE|HV_FETCH_JUST_SV), val, 0);
+ (HV_FETCH_ISSTORE|HV_FETCH_JUST_SV), val, hash);
return hek ? &HeVAL(hek) : NULL;
}
(lval ? HV_FETCH_LVALUE : 0), Nullsv, hash);
}
-HE *
+STATIC HE *
S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
int flags, int action, SV *val, register U32 hash)
{
+ dVAR;
XPVHV* xhv;
U32 n_links;
HE *entry;
return 0;
if (keysv) {
+ if (flags & HVhek_FREEKEY)
+ Safefree(key);
key = SvPV(keysv, klen);
flags = 0;
is_utf8 = (SvUTF8(keysv) != 0);
U32 i;
for (i = 0; i < klen; ++i)
if (isLOWER(key[i])) {
- const char *keysave = key;
- /* Will need to free this, so set FREEKEY flag
- on call to hv_fetch_common. */
- key = savepvn(key,klen);
- key = (const char*)strupr((char*)key);
-
- if (flags & HVhek_FREEKEY)
- Safefree(keysave);
-
- /* This isn't strictly the same as the old hv_fetch
- magic, which made a call to hv_fetch, followed
- by a call to hv_store if that failed and lvalue
- was true.
- Which I believe could have been done by simply
- passing the lvalue through to the first hv_fetch.
- So I will do that here. */
- return hv_fetch_common(hv, Nullsv, key, klen,
- HVhek_FREEKEY,
- action, Nullsv, 0);
+ /* Would be nice if we had a routine to do the
+ copy and upercase in a single pass through. */
+ const char *nkey = strupr(savepvn(key,klen));
+ /* Note that this fetch is for nkey (the uppercased
+ key) whereas the store is for key (the original) */
+ entry = hv_fetch_common(hv, Nullsv, nkey, klen,
+ HVhek_FREEKEY, /* free nkey */
+ 0 /* non-LVAL fetch */,
+ Nullsv /* no value */,
+ 0 /* compute hash */);
+ if (!entry && (action & HV_FETCH_LVALUE)) {
+ /* This call will free key if necessary.
+ Do it this way to encourage compiler to tail
+ call optimise. */
+ entry = hv_fetch_common(hv, keysv, key, klen,
+ flags, HV_FETCH_ISSTORE,
+ NEWSV(61,0), hash);
+ } else {
+ if (flags & HVhek_FREEKEY)
+ Safefree(key);
+ }
+ return entry;
}
}
#endif
key = (const char*)strupr((char*)key);
is_utf8 = 0;
hash = 0;
+ keysv = 0;
if (flags & HVhek_FREEKEY) {
Safefree(keysave);
bool needs_store;
hv_magic_check (hv, &needs_copy, &needs_store);
if (needs_copy) {
- bool save_taint = PL_tainted;
+ const bool save_taint = PL_tainted;
if (keysv || is_utf8) {
if (!keysv) {
keysv = newSVpvn(key, klen);
}
TAINT_IF(save_taint);
- if (!xhv->xhv_array /* !HvARRAY(hv) */ && !needs_store) {
+ if (!HvARRAY(hv) && !needs_store) {
if (flags & HVhek_FREEKEY)
Safefree(key);
return Nullhe;
key = (const char*)strupr((char*)key);
is_utf8 = 0;
hash = 0;
+ keysv = 0;
if (flags & HVhek_FREEKEY) {
Safefree(keysave);
} /* ISSTORE */
} /* SvMAGICAL */
- if (!xhv->xhv_array /* !HvARRAY(hv) */) {
+ 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((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env))
#endif
)
- Newz(503, xhv->xhv_array /* HvARRAY(hv) */,
+ Newz(503, HvARRAY(hv),
PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
- char);
+ HE*);
#ifdef DYNAMIC_ENV_FETCH
else if (action & HV_FETCH_ISEXISTS) {
/* for an %ENV exists, if we do an insert it's by a recursive
n_links = 0;
#ifdef DYNAMIC_ENV_FETCH
- if (!xhv->xhv_array /* !HvARRAY(hv) */) entry = Null(HE*);
+ if (!HvARRAY(hv)) entry = Null(HE*);
else
#endif
{
- /* entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
- entry = ((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
+ entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)];
}
for (; entry; ++n_links, entry = HeNEXT(entry)) {
if (HeHASH(entry) != hash) /* strings can't be equal */
}
/* LVAL fetch which actaully needs a store. */
val = NEWSV(61,0);
- xhv->xhv_placeholders--;
+ HvPLACEHOLDERS(hv)--;
} else {
/* store */
if (val != &PL_sv_placeholder)
- xhv->xhv_placeholders--;
+ HvPLACEHOLDERS(hv)--;
}
HeVAL(entry) = val;
} else if (action & HV_FETCH_ISSTORE) {
if (!entry && SvREADONLY(hv) && !(action & HV_FETCH_ISEXISTS)) {
S_hv_notallowed(aTHX_ flags, key, klen,
- "access disallowed key '%"SVf"' in"
- );
+ "Attempt to access disallowed key '%"SVf"' in"
+ " a restricted hash");
}
if (!(action & (HV_FETCH_LVALUE|HV_FETCH_ISSTORE))) {
/* Not doing some form of store, so return failure. */
/* Welcome to hv_store... */
- if (!xhv->xhv_array) {
+ if (!HvARRAY(hv)) {
/* Not sure if we can get here. I think the only case of oentry being
NULL is for %ENV with dynamic env fetch. But that should disappear
with magic in the previous code. */
- Newz(503, xhv->xhv_array /* HvARRAY(hv) */,
+ Newz(503, HvARRAY(hv),
PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
- char);
+ HE*);
}
- oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
+ oentry = &(HvARRAY(hv))[hash & (I32) xhv->xhv_max];
entry = new_HE();
/* share_hek_flags will do the free for us. This might be considered
*oentry = entry;
if (val == &PL_sv_placeholder)
- xhv->xhv_placeholders++;
+ HvPLACEHOLDERS(hv)++;
if (masked_flags & HVhek_ENABLEHVKFLAGS)
HvHASKFLAGS_on(hv);
STATIC void
S_hv_magic_check(pTHX_ HV *hv, bool *needs_copy, bool *needs_store)
{
- MAGIC *mg = SvMAGIC(hv);
+ const MAGIC *mg = SvMAGIC(hv);
*needs_copy = FALSE;
*needs_store = TRUE;
while (mg) {
return hv_delete_common(hv, keysv, NULL, 0, 0, flags, hash);
}
-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)
{
+ dVAR;
register XPVHV* xhv;
register I32 i;
register HE *entry;
return Nullsv;
if (keysv) {
+ if (k_flags & HVhek_FREEKEY)
+ Safefree(key);
key = SvPV(keysv, klen);
k_flags = 0;
is_utf8 = (SvUTF8(keysv) != 0);
}
return Nullsv; /* element cannot be deleted */
}
- }
#ifdef ENV_IS_CASELESS
- else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
- /* XXX This code isn't UTF8 clean. */
- keysv = sv_2mortal(newSVpvn(key,klen));
- key = strupr(SvPVX(keysv));
-
-#if 0
- /* keysave not in scope - don't understand - NI-S */
- if (k_flags & HVhek_FREEKEY) {
- Safefree(keysave);
+ else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
+ /* XXX This code isn't UTF8 clean. */
+ keysv = sv_2mortal(newSVpvn(key,klen));
+ if (k_flags & HVhek_FREEKEY) {
+ Safefree(key);
+ }
+ key = strupr(SvPVX(keysv));
+ is_utf8 = 0;
+ k_flags = 0;
+ hash = 0;
}
#endif
-
- is_utf8 = 0;
- k_flags = 0;
- hash = 0;
}
-#endif
}
}
xhv = (XPVHV*)SvANY(hv);
- if (!xhv->xhv_array /* !HvARRAY(hv) */)
+ if (!HvARRAY(hv))
return Nullsv;
if (is_utf8) {
} else {
PERL_HASH(hash, key, klen);
}
- PERL_HASH(hash, key, klen);
}
masked_flags = (k_flags & HVhek_MASK);
- /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
- oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
+ oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)];
entry = *oentry;
i = 1;
for (; entry; i=0, oentry = &HeNEXT(entry), entry = *oentry) {
continue;
if ((HeKFLAGS(entry) ^ masked_flags) & HVhek_UTF8)
continue;
- if (k_flags & HVhek_FREEKEY)
- Safefree(key);
/* if placeholder is here, it's already been deleted.... */
if (HeVAL(entry) == &PL_sv_placeholder)
{
- if (SvREADONLY(hv))
- return Nullsv; /* if still SvREADONLY, leave it deleted. */
-
- /* okay, really delete the placeholder. */
- *oentry = HeNEXT(entry);
- if (i && !*oentry)
- xhv->xhv_fill--; /* HvFILL(hv)-- */
- if (entry == xhv->xhv_eiter /* HvEITER(hv) */)
- HvLAZYDEL_on(hv);
- else
- hv_free_ent(hv, entry);
- xhv->xhv_keys--; /* HvKEYS(hv)-- */
- if (xhv->xhv_keys == 0)
- HvHASKFLAGS_off(hv);
- xhv->xhv_placeholders--;
- return Nullsv;
+ if (k_flags & HVhek_FREEKEY)
+ Safefree(key);
+ return Nullsv;
}
else if (SvREADONLY(hv) && HeVAL(entry) && SvREADONLY(HeVAL(entry))) {
S_hv_notallowed(aTHX_ k_flags, key, klen,
- "delete readonly key '%"SVf"' from"
- );
+ "Attempt to delete readonly key '%"SVf"' from"
+ " a restricted hash");
}
+ if (k_flags & HVhek_FREEKEY)
+ Safefree(key);
if (d_flags & G_DISCARD)
sv = Nullsv;
* an error.
*/
if (SvREADONLY(hv)) {
+ SvREFCNT_dec(HeVAL(entry));
HeVAL(entry) = &PL_sv_placeholder;
/* We'll be saving this slot, so the number of allocated keys
* doesn't go down, but the number placeholders goes up */
- xhv->xhv_placeholders++; /* HvPLACEHOLDERS(hv)++ */
+ HvPLACEHOLDERS(hv)++;
} else {
*oentry = HeNEXT(entry);
if (i && !*oentry)
xhv->xhv_fill--; /* HvFILL(hv)-- */
- if (entry == xhv->xhv_eiter /* HvEITER(hv) */)
+ if (xhv->xhv_aux && entry
+ == ((struct xpvhv_aux *)xhv->xhv_aux)->xhv_eiter /* HvEITER(hv) */)
HvLAZYDEL_on(hv);
else
hv_free_ent(hv, entry);
}
if (SvREADONLY(hv)) {
S_hv_notallowed(aTHX_ k_flags, key, klen,
- "delete disallowed key '%"SVf"' from"
- );
+ "Attempt to delete disallowed key '%"SVf"' from"
+ " a restricted hash");
}
if (k_flags & HVhek_FREEKEY)
S_hsplit(pTHX_ HV *hv)
{
register XPVHV* xhv = (XPVHV*)SvANY(hv);
- I32 oldsize = (I32) xhv->xhv_max+1; /* HvMAX(hv)+1 (sick) */
+ const I32 oldsize = (I32) xhv->xhv_max+1; /* HvMAX(hv)+1 (sick) */
register I32 newsize = oldsize * 2;
register I32 i;
- register char *a = xhv->xhv_array; /* HvARRAY(hv) */
+ char *a = (char*) HvARRAY(hv);
register HE **aep;
- register HE **bep;
- register HE *entry;
register HE **oentry;
int longest_chain = 0;
int was_shared;
+ /*PerlIO_printf(PerlIO_stderr(), "hsplit called for %p which had %d\n",
+ hv, (int) oldsize);*/
+
+ if (HvPLACEHOLDERS(hv) && !SvREADONLY(hv)) {
+ /* Can make this clear any placeholders first for non-restricted hashes,
+ even though Storable rebuilds restricted hashes by putting in all the
+ placeholders (first) before turning on the readonly flag, because
+ Storable always pre-splits the hash. */
+ hv_clear_placeholders(hv);
+ }
+
PL_nomemok = TRUE;
#if defined(STRANGE_MALLOC) || defined(MYMALLOC)
Renew(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
PL_nomemok = FALSE;
return;
}
- Copy(xhv->xhv_array /* HvARRAY(hv) */, a, oldsize * sizeof(HE*), char);
+ Copy(HvARRAY(hv), a, oldsize * sizeof(HE*), char);
if (oldsize >= 64) {
- offer_nice_chunk(xhv->xhv_array /* HvARRAY(hv) */,
+ offer_nice_chunk(HvARRAY(hv),
PERL_HV_ARRAY_ALLOC_BYTES(oldsize));
}
else
- Safefree(xhv->xhv_array /* HvARRAY(hv) */);
+ Safefree(HvARRAY(hv));
#endif
PL_nomemok = FALSE;
Zero(&a[oldsize * sizeof(HE*)], (newsize-oldsize) * sizeof(HE*), char); /* zero 2nd half*/
xhv->xhv_max = --newsize; /* HvMAX(hv) = --newsize */
- xhv->xhv_array = a; /* HvARRAY(hv) = a */
+ HvARRAY(hv) = (HE**) a;
aep = (HE**)a;
for (i=0; i<oldsize; i++,aep++) {
int left_length = 0;
int right_length = 0;
+ register HE *entry;
+ register HE **bep;
if (!*aep) /* non-existent */
continue;
HvSHAREKEYS_off(hv);
HvREHASH_on(hv);
- aep = (HE **) xhv->xhv_array;
+ aep = HvARRAY(hv);
for (i=0; i<newsize; i++,aep++) {
- entry = *aep;
+ register HE *entry = *aep;
while (entry) {
/* We're going to trash this HE's next pointer when we chain it
into the new hash below, so store where we go next. */
HE *next = HeNEXT(entry);
UV hash;
+ HE **bep;
/* Rehash it */
PERL_HASH_INTERNAL(hash, HeKEY(entry), HeKLEN(entry));
entry = next;
}
}
- Safefree (xhv->xhv_array);
- xhv->xhv_array = a; /* HvARRAY(hv) = a */
+ Safefree (HvARRAY(hv));
+ HvARRAY(hv) = (HE **)a;
}
void
Perl_hv_ksplit(pTHX_ HV *hv, IV newmax)
{
register XPVHV* xhv = (XPVHV*)SvANY(hv);
- I32 oldsize = (I32) xhv->xhv_max+1; /* HvMAX(hv)+1 (sick) */
+ const I32 oldsize = (I32) xhv->xhv_max+1; /* HvMAX(hv)+1 (sick) */
register I32 newsize;
register I32 i;
- register I32 j;
register char *a;
register HE **aep;
register HE *entry;
if (newsize < newmax)
return; /* overflow detection */
- a = xhv->xhv_array; /* HvARRAY(hv) */
+ a = (char *) HvARRAY(hv);
if (a) {
PL_nomemok = TRUE;
#if defined(STRANGE_MALLOC) || defined(MYMALLOC)
PL_nomemok = FALSE;
return;
}
- Copy(xhv->xhv_array /* HvARRAY(hv) */, a, oldsize * sizeof(HE*), char);
+ Copy(HvARRAY(hv), a, oldsize * sizeof(HE*), char);
if (oldsize >= 64) {
- offer_nice_chunk(xhv->xhv_array /* HvARRAY(hv) */,
+ offer_nice_chunk(HvARRAY(hv),
PERL_HV_ARRAY_ALLOC_BYTES(oldsize));
}
else
- Safefree(xhv->xhv_array /* HvARRAY(hv) */);
+ Safefree(HvARRAY(hv));
#endif
PL_nomemok = FALSE;
Zero(&a[oldsize * sizeof(HE*)], (newsize-oldsize) * sizeof(HE*), char); /* zero 2nd half*/
Newz(0, a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
}
xhv->xhv_max = --newsize; /* HvMAX(hv) = --newsize */
- xhv->xhv_array = a; /* HvARRAY(hv) = a */
+ HvARRAY(hv) = (HE **) a;
if (!xhv->xhv_fill /* !HvFILL(hv) */) /* skip rest if no entries */
return;
if (!*aep) /* non-existent */
continue;
for (oentry = aep, entry = *aep; entry; entry = *oentry) {
+ register I32 j;
if ((j = (HeHASH(entry) & newsize)) != i) {
j -= i;
*oentry = HeNEXT(entry);
xhv->xhv_max = 7; /* HvMAX(hv) = 7 (start with 8 buckets) */
xhv->xhv_fill = 0; /* HvFILL(hv) = 0 */
- xhv->xhv_pmroot = 0; /* HvPMROOT(hv) = 0 */
- (void)hv_iterinit(hv); /* so each() will start off right */
+ xhv->xhv_aux = 0;
return hv;
}
if (!SvMAGICAL((SV *)ohv)) {
/* It's an ordinary hash, so copy it fast. AMS 20010804 */
STRLEN i;
- bool shared = !!HvSHAREKEYS(ohv);
+ const bool shared = !!HvSHAREKEYS(ohv);
HE **ents, **oents = (HE **)HvARRAY(ohv);
char *a;
New(0, a, PERL_HV_ARRAY_ALLOC_BYTES(hv_max+1), char);
/* Copy the linked list of entries. */
for (oent = oents[i]; oent; oent = HeNEXT(oent)) {
- U32 hash = HeHASH(oent);
- char *key = HeKEY(oent);
- STRLEN len = HeKLEN(oent);
- int flags = HeKFLAGS(oent);
+ const U32 hash = HeHASH(oent);
+ const char * const key = HeKEY(oent);
+ const STRLEN len = HeKLEN(oent);
+ const int flags = HeKFLAGS(oent);
ent = new_HE();
HeVAL(ent) = newSVsv(HeVAL(oent));
else {
/* Iterate over ohv, copying keys and values one at a time. */
HE *entry;
- I32 riter = HvRITER(ohv);
- HE *eiter = HvEITER(ohv);
+ const I32 riter = HvRITER_get(ohv);
+ HE * const eiter = HvEITER_get(ohv);
/* Can we use fewer buckets? (hv_max is always 2^n-1) */
while (hv_max && hv_max + 1 >= hv_fill * 2)
newSVsv(HeVAL(entry)), HeHASH(entry),
HeKFLAGS(entry));
}
- HvRITER(ohv) = riter;
- HvEITER(ohv) = eiter;
+ HvRITER_set(ohv, riter);
+ HvEITER_set(ohv, eiter);
}
return hv;
if (!entry)
return;
val = HeVAL(entry);
- if (val && isGV(val) && GvCVu(val) && HvNAME(hv))
+ if (val && isGV(val) && GvCVu(val) && HvNAME_get(hv))
PL_sub_generation++; /* may be deletion of method from stash */
SvREFCNT_dec(val);
if (HeKLEN(entry) == HEf_SVKEY) {
{
if (!entry)
return;
- if (isGV(HeVAL(entry)) && GvCVu(HeVAL(entry)) && HvNAME(hv))
+ if (isGV(HeVAL(entry)) && GvCVu(HeVAL(entry)) && HvNAME_get(hv))
PL_sub_generation++; /* may be deletion of method from stash */
sv_2mortal(HeVAL(entry)); /* free between statements */
if (HeKLEN(entry) == HEf_SVKEY) {
void
Perl_hv_clear(pTHX_ HV *hv)
{
+ dVAR;
register XPVHV* xhv;
if (!hv)
return;
xhv = (XPVHV*)SvANY(hv);
- if (SvREADONLY(hv) && xhv->xhv_array != NULL) {
+ if (SvREADONLY(hv) && HvARRAY(hv) != NULL) {
/* restricted hash: convert all keys to placeholders */
I32 i;
- HE* entry;
for (i = 0; i <= (I32) xhv->xhv_max; i++) {
- entry = ((HE**)xhv->xhv_array)[i];
+ HE *entry = (HvARRAY(hv))[i];
for (; entry; entry = HeNEXT(entry)) {
/* not already placeholder */
if (HeVAL(entry) != &PL_sv_placeholder) {
}
SvREFCNT_dec(HeVAL(entry));
HeVAL(entry) = &PL_sv_placeholder;
- xhv->xhv_placeholders++; /* HvPLACEHOLDERS(hv)++ */
+ HvPLACEHOLDERS(hv)++;
}
}
}
}
hfreeentries(hv);
- xhv->xhv_placeholders = 0; /* HvPLACEHOLDERS(hv) = 0 */
- if (xhv->xhv_array /* HvARRAY(hv) */)
- (void)memzero(xhv->xhv_array /* HvARRAY(hv) */,
+ HvPLACEHOLDERS_set(hv, 0);
+ if (HvARRAY(hv))
+ (void)memzero(HvARRAY(hv),
(xhv->xhv_max+1 /* HvMAX(hv)+1 */) * sizeof(HE*));
if (SvRMAGICAL(hv))
HvHASKFLAGS_off(hv);
HvREHASH_off(hv);
reset:
- HvEITER(hv) = NULL;
+ if (xhv->xhv_aux) {
+ HvEITER_set(hv, NULL);
+ }
}
/*
marked as readonly and the key is subsequently deleted, the key is not actually
deleted but is marked by assigning it a value of &PL_sv_placeholder. This tags
it so it will be ignored by future operations such as iterating over the hash,
-but will still allow the hash to have a value reaasigned to the key at some
+but will still allow the hash to have a value reassigned to the key at some
future point. This function clears any such placeholder keys from the hash.
See Hash::Util::lock_keys() for an example of its use.
void
Perl_hv_clear_placeholders(pTHX_ HV *hv)
{
- I32 items;
- items = (I32)HvPLACEHOLDERS(hv);
- if (items) {
- HE *entry;
- I32 riter = HvRITER(hv);
- HE *eiter = HvEITER(hv);
- hv_iterinit(hv);
- /* This may look suboptimal with the items *after* the iternext, but
- it's quite deliberate. We only get here with items==0 if we've
- just deleted the last placeholder in the hash. If we've just done
- that then it means that the hash is in lazy delete mode, and the
- HE is now only referenced in our iterator. If we just quit the loop
- and discarded our iterator then the HE leaks. So we do the && the
- other way to ensure iternext is called just one more time, which
- has the side effect of triggering the lazy delete. */
- while ((entry = hv_iternext_flags(hv, HV_ITERNEXT_WANTPLACEHOLDERS))
- && items) {
- SV *val = hv_iterval(hv, entry);
-
- if (val == &PL_sv_placeholder) {
-
- /* It seems that I have to go back in the front of the hash
- API to delete a hash, even though I have a HE structure
- pointing to the very entry I want to delete, and could hold
- onto the previous HE that points to it. And it's easier to
- go in with SVs as I can then specify the precomputed hash,
- and don't have fun and games with utf8 keys. */
- SV *key = hv_iterkeysv(entry);
-
- hv_delete_ent (hv, key, G_DISCARD, HeHASH(entry));
- items--;
- }
- }
- HvRITER(hv) = riter;
- HvEITER(hv) = eiter;
- }
+ dVAR;
+ I32 items = (I32)HvPLACEHOLDERS(hv);
+ I32 i = HvMAX(hv);
+
+ if (items == 0)
+ return;
+
+ do {
+ /* Loop down the linked list heads */
+ bool first = 1;
+ HE **oentry = &(HvARRAY(hv))[i];
+ HE *entry = *oentry;
+
+ if (!entry)
+ continue;
+
+ for (; entry; entry = *oentry) {
+ if (HeVAL(entry) == &PL_sv_placeholder) {
+ *oentry = HeNEXT(entry);
+ if (first && !*oentry)
+ HvFILL(hv)--; /* This linked list is now empty. */
+ if (HvEITER_get(hv))
+ HvLAZYDEL_on(hv);
+ else
+ hv_free_ent(hv, entry);
+
+ if (--items == 0) {
+ /* Finished. */
+ HvTOTALKEYS(hv) -= (IV)HvPLACEHOLDERS(hv);
+ if (HvKEYS(hv) == 0)
+ HvHASKFLAGS_off(hv);
+ HvPLACEHOLDERS(hv) = 0;
+ return;
+ }
+ } else {
+ oentry = &HeNEXT(entry);
+ first = 0;
+ }
+ }
+ } while (--i >= 0);
+ /* You can't get here, hence assertion should always fail. */
+ assert (items == 0);
+ assert (0);
}
STATIC void
{
register HE **array;
register HE *entry;
- register HE *oentry = Null(HE*);
I32 riter;
I32 max;
+ struct xpvhv_aux *iter;
if (!hv)
return;
entry = array[0];
for (;;) {
if (entry) {
- oentry = entry;
+ register HE *oentry = entry;
entry = HeNEXT(entry);
hv_free_ent(hv, oentry);
}
}
}
HvARRAY(hv) = array;
- (void)hv_iterinit(hv);
+
+ iter = ((XPVHV*) SvANY(hv))->xhv_aux;
+ if (iter) {
+ entry = iter->xhv_eiter; /* HvEITER(hv) */
+ if (entry && HvLAZYDEL(hv)) { /* was deleted earlier? */
+ HvLAZYDEL_off(hv);
+ hv_free_ent(hv, entry);
+ }
+ Safefree(iter);
+ ((XPVHV*) SvANY(hv))->xhv_aux = 0;
+ }
}
/*
Perl_hv_undef(pTHX_ HV *hv)
{
register XPVHV* xhv;
+ const char *name;
if (!hv)
return;
DEBUG_A(Perl_hv_assert(aTHX_ hv));
xhv = (XPVHV*)SvANY(hv);
hfreeentries(hv);
- Safefree(xhv->xhv_array /* HvARRAY(hv) */);
- if (HvNAME(hv)) {
+ Safefree(HvARRAY(hv));
+ if ((name = HvNAME_get(hv))) {
+ /* FIXME - strlen HvNAME */
if(PL_stashcache)
- hv_delete(PL_stashcache, HvNAME(hv), strlen(HvNAME(hv)), G_DISCARD);
- Safefree(HvNAME(hv));
- HvNAME(hv) = 0;
+ hv_delete(PL_stashcache, name, strlen(name), G_DISCARD);
+ Perl_hv_name_set(aTHX_ hv, 0, 0, 0);
}
xhv->xhv_max = 7; /* HvMAX(hv) = 7 (it's a normal hash) */
- xhv->xhv_array = 0; /* HvARRAY(hv) = 0 */
- xhv->xhv_placeholders = 0; /* HvPLACEHOLDERS(hv) = 0 */
+ HvARRAY(hv) = 0;
+ HvPLACEHOLDERS_set(hv, 0);
if (SvRMAGICAL(hv))
mg_clear((SV*)hv);
}
+struct xpvhv_aux*
+S_hv_auxinit(aTHX) {
+ struct xpvhv_aux *iter;
+
+ New(0, iter, 1, struct xpvhv_aux);
+
+ iter->xhv_riter = -1; /* HvRITER(hv) = -1 */
+ iter->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */
+ iter->xhv_name = 0;
+
+ return iter;
+}
+
/*
=for apidoc hv_iterinit
{
register XPVHV* xhv;
HE *entry;
+ struct xpvhv_aux *iter;
if (!hv)
Perl_croak(aTHX_ "Bad hash");
xhv = (XPVHV*)SvANY(hv);
- entry = xhv->xhv_eiter; /* HvEITER(hv) */
- if (entry && HvLAZYDEL(hv)) { /* was deleted earlier? */
- HvLAZYDEL_off(hv);
- hv_free_ent(hv, entry);
+
+ iter = xhv->xhv_aux;
+ if (iter) {
+ entry = iter->xhv_eiter; /* HvEITER(hv) */
+ if (entry && HvLAZYDEL(hv)) { /* was deleted earlier? */
+ HvLAZYDEL_off(hv);
+ hv_free_ent(hv, entry);
+ }
+ iter->xhv_riter = -1; /* HvRITER(hv) = -1 */
+ iter->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */
+ } else {
+ xhv->xhv_aux = S_hv_auxinit(aTHX);
}
- xhv->xhv_riter = -1; /* HvRITER(hv) = -1 */
- xhv->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */
+
/* used to be xhv->xhv_fill before 5.004_65 */
return XHvTOTALKEYS(xhv);
}
+
+I32 *
+Perl_hv_riter_p(pTHX_ HV *hv) {
+ struct xpvhv_aux *iter;
+
+ if (!hv)
+ Perl_croak(aTHX_ "Bad hash");
+
+ iter = ((XPVHV *)SvANY(hv))->xhv_aux;
+ if (!iter) {
+ ((XPVHV *)SvANY(hv))->xhv_aux = iter = S_hv_auxinit(aTHX);
+ }
+ return &(iter->xhv_riter);
+}
+
+HE **
+Perl_hv_eiter_p(pTHX_ HV *hv) {
+ struct xpvhv_aux *iter;
+
+ if (!hv)
+ Perl_croak(aTHX_ "Bad hash");
+
+ iter = ((XPVHV *)SvANY(hv))->xhv_aux;
+ if (!iter) {
+ ((XPVHV *)SvANY(hv))->xhv_aux = iter = S_hv_auxinit(aTHX);
+ }
+ return &(iter->xhv_eiter);
+}
+
+void
+Perl_hv_riter_set(pTHX_ HV *hv, I32 riter) {
+ struct xpvhv_aux *iter;
+
+ if (!hv)
+ Perl_croak(aTHX_ "Bad hash");
+
+
+ iter = ((XPVHV *)SvANY(hv))->xhv_aux;
+ if (!iter) {
+ if (riter == -1)
+ return;
+
+ ((XPVHV *)SvANY(hv))->xhv_aux = iter = S_hv_auxinit(aTHX);
+ }
+ iter->xhv_riter = riter;
+}
+
+void
+Perl_hv_eiter_set(pTHX_ HV *hv, HE *eiter) {
+ struct xpvhv_aux *iter;
+
+ if (!hv)
+ Perl_croak(aTHX_ "Bad hash");
+
+ iter = ((XPVHV *)SvANY(hv))->xhv_aux;
+ if (!iter) {
+ /* 0 is the default so don't go malloc()ing a new structure just to
+ hold 0. */
+ if (!eiter)
+ return;
+
+ ((XPVHV *)SvANY(hv))->xhv_aux = iter = S_hv_auxinit(aTHX);
+ }
+ iter->xhv_eiter = eiter;
+}
+
+
+char **
+Perl_hv_name_p(pTHX_ HV *hv)
+{
+ struct xpvhv_aux *iter = ((XPVHV *)SvANY(hv))->xhv_aux;
+
+ if (!iter) {
+ ((XPVHV *)SvANY(hv))->xhv_aux = iter = S_hv_auxinit(aTHX);
+ }
+ return &(iter->xhv_name);
+}
+
+void
+Perl_hv_name_set(pTHX_ HV *hv, const char *name, STRLEN len, int flags)
+{
+ struct xpvhv_aux *iter = ((XPVHV *)SvANY(hv))->xhv_aux;
+
+ if (!iter) {
+ if (name == 0)
+ return;
+
+ ((XPVHV *)SvANY(hv))->xhv_aux = iter = S_hv_auxinit(aTHX);
+ }
+ iter->xhv_name = savepvn(name, len);
+}
+
/*
=for apidoc hv_iternext
HE *
Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
{
+ dVAR;
register XPVHV* xhv;
register HE *entry;
HE *oldentry;
MAGIC* mg;
+ struct xpvhv_aux *iter;
if (!hv)
Perl_croak(aTHX_ "Bad hash");
xhv = (XPVHV*)SvANY(hv);
- oldentry = entry = xhv->xhv_eiter; /* HvEITER(hv) */
+ iter = xhv->xhv_aux;
+
+ if (!iter) {
+ /* Too many things (well, pp_each at least) merrily assume that you can
+ call iv_iternext without calling hv_iterinit, so we'll have to deal
+ with it. */
+ hv_iterinit(hv);
+ iter = ((XPVHV *)SvANY(hv))->xhv_aux;
+ }
+
+ oldentry = entry = iter->xhv_eiter; /* HvEITER(hv) */
if ((mg = SvTIED_mg((SV*)hv, PERL_MAGIC_tied))) {
SV *key = sv_newmortal();
HEK *hek;
/* one HE per MAGICAL hash */
- xhv->xhv_eiter = entry = new_HE(); /* HvEITER(hv) = new_HE() */
+ iter->xhv_eiter = entry = new_HE(); /* HvEITER(hv) = new_HE() */
Zero(entry, 1, HE);
Newz(54, k, HEK_BASESIZE + sizeof(SV*), char);
hek = (HEK*)k;
SvREFCNT_dec(HeVAL(entry));
Safefree(HeKEY_hek(entry));
del_HE(entry);
- xhv->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */
+ iter->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */
return Null(HE*);
}
#ifdef DYNAMIC_ENV_FETCH /* set up %ENV for iteration */
prime_env_iter();
#endif
- if (!xhv->xhv_array /* !HvARRAY(hv) */)
- Newz(506, xhv->xhv_array /* HvARRAY(hv) */,
+ if (!HvARRAY(hv)) {
+ char *darray;
+ Newz(506, darray,
PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
char);
+ HvARRAY(hv) = (HE**) darray;
+ }
/* At start of hash, entry is NULL. */
if (entry)
{
while (!entry) {
/* OK. Come to the end of the current list. Grab the next one. */
- xhv->xhv_riter++; /* HvRITER(hv)++ */
- if (xhv->xhv_riter > (I32)xhv->xhv_max /* HvRITER(hv) > HvMAX(hv) */) {
+ iter->xhv_riter++; /* HvRITER(hv)++ */
+ if (iter->xhv_riter > (I32)xhv->xhv_max /* HvRITER(hv) > HvMAX(hv) */) {
/* There is no next one. End of the hash. */
- xhv->xhv_riter = -1; /* HvRITER(hv) = -1 */
+ iter->xhv_riter = -1; /* HvRITER(hv) = -1 */
break;
}
- /* entry = (HvARRAY(hv))[HvRITER(hv)]; */
- entry = ((HE**)xhv->xhv_array)[xhv->xhv_riter];
+ entry = (HvARRAY(hv))[iter->xhv_riter];
if (!(flags & HV_ITERNEXT_WANTPLACEHOLDERS)) {
/* If we have an entry, but it's a placeholder, don't count it.
/*if (HvREHASH(hv) && entry && !HeKREHASH(entry))
PerlIO_printf(PerlIO_stderr(), "Awooga %p %p\n", hv, entry);*/
- xhv->xhv_eiter = entry; /* HvEITER(hv) = entry */
+ iter->xhv_eiter = entry; /* HvEITER(hv) = entry */
return entry;
}
{
if (HeKLEN(entry) != HEf_SVKEY) {
HEK *hek = HeKEY_hek(entry);
- int flags = HEK_FLAGS(hek);
+ const int flags = HEK_FLAGS(hek);
SV *sv;
if (flags & HVhek_WASUTF8) {
SV* sv = sv_newmortal();
if (HeKLEN(entry) == HEf_SVKEY)
mg_copy((SV*)hv, sv, (char*)HeKEY_sv(entry), HEf_SVKEY);
- else mg_copy((SV*)hv, sv, HeKEY(entry), HeKLEN(entry));
+ else
+ mg_copy((SV*)hv, sv, HeKEY(entry), HeKLEN(entry));
return sv;
}
}
register HE *entry;
register HE **oentry;
register I32 i = 1;
- I32 found = 0;
+ bool found = 0;
bool is_utf8 = FALSE;
int k_flags = 0;
const char *save = str;
xhv = (XPVHV*)SvANY(PL_strtab);
/* assert(xhv_array != 0) */
LOCK_STRTAB_MUTEX;
- /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
- oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
+ oentry = &(HvARRAY(PL_strtab))[hash & (I32) HvMAX(PL_strtab)];
if (hek) {
for (entry = *oentry; entry; i=0, oentry = &HeNEXT(entry), entry = *oentry) {
if (HeKEY_hek(entry) != hek)
break;
}
} else {
- int flags_masked = k_flags & HVhek_MASK;
+ const int flags_masked = k_flags & HVhek_MASK;
for (entry = *oentry; entry; i=0, oentry = &HeNEXT(entry), entry = *oentry) {
if (HeHASH(entry) != hash) /* strings can't be equal */
continue;
UNLOCK_STRTAB_MUTEX;
if (!found && ckWARN_d(WARN_INTERNAL))
Perl_warner(aTHX_ packWARN(WARN_INTERNAL),
- "Attempt to free non-existent shared string '%s'%s",
+ "Attempt to free non-existent shared string '%s'%s"
+ pTHX__FORMAT,
hek ? HEK_KEY(hek) : str,
- (k_flags & HVhek_UTF8) ? " (utf8)" : "");
+ ((k_flags & HVhek_UTF8) ? " (utf8)" : "") pTHX__VALUE);
if (k_flags & HVhek_FREEKEY)
Safefree(str);
}
register HE **oentry;
register I32 i = 1;
I32 found = 0;
- int flags_masked = flags & HVhek_MASK;
+ const int flags_masked = flags & HVhek_MASK;
/* what follows is the moral equivalent of:
xhv = (XPVHV*)SvANY(PL_strtab);
/* assert(xhv_array != 0) */
LOCK_STRTAB_MUTEX;
- /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
- oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
+ oentry = &(HvARRAY(PL_strtab))[hash & (I32) HvMAX(PL_strtab)];
for (entry = *oentry; entry; i=0, entry = HeNEXT(entry)) {
if (HeHASH(entry) != hash) /* strings can't be equal */
continue;
}
if (!found) {
entry = new_HE();
- HeKEY_hek(entry) = save_hek_flags(str, len, hash, flags);
+ HeKEY_hek(entry) = save_hek_flags(str, len, hash, flags_masked);
HeVAL(entry) = Nullsv;
HeNEXT(entry) = *oentry;
*oentry = entry;
return HeKEY_hek(entry);
}
+I32 *
+Perl_hv_placeholders_p(pTHX_ HV *hv)
+{
+ dVAR;
+ MAGIC *mg = mg_find((SV*)hv, PERL_MAGIC_rhash);
+
+ if (!mg) {
+ mg = sv_magicext((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_ HV *hv)
+{
+ dVAR;
+ MAGIC *mg = mg_find((SV*)hv, PERL_MAGIC_rhash);
+
+ return mg ? mg->mg_len : 0;
+}
+
+void
+Perl_hv_placeholders_set(pTHX_ HV *hv, I32 ph)
+{
+ dVAR;
+ MAGIC *mg = mg_find((SV*)hv, PERL_MAGIC_rhash);
+
+ if (mg) {
+ mg->mg_len = ph;
+ } else if (ph) {
+ if (!sv_magicext((SV*)hv, 0, PERL_MAGIC_rhash, 0, 0, ph))
+ Perl_die(aTHX_ "panic: hv_placeholders_set");
+ }
+ /* else we don't need to add magic to record 0 placeholders. */
+}
/*
=for apidoc hv_assert
void
Perl_hv_assert(pTHX_ HV *hv)
{
+ dVAR;
HE* entry;
int withflags = 0;
int placeholders = 0;
int real = 0;
int bad = 0;
- I32 riter = HvRITER(hv);
- HE *eiter = HvEITER(hv);
+ const I32 riter = HvRITER_get(hv);
+ HE *eiter = HvEITER_get(hv);
(void)hv_iterinit(hv);
if (bad) {
sv_dump((SV *)hv);
}
- HvRITER(hv) = riter; /* Restore hash iterator state */
- HvEITER(hv) = eiter;
+ HvRITER_set(hv, riter); /* Restore hash iterator state */
+ HvEITER_set(hv, eiter);
}
+
+/*
+ * Local variables:
+ * c-indentation-style: bsd
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ *
+ * ex: set ts=8 sts=4 sw=4 noet:
+ */