#define HV_MAX_LENGTH_BEFORE_SPLIT 14
+static const char S_strtab_error[]
+ = "Cannot modify shared string table in hv_%s";
+
STATIC void
S_more_he(pTHX)
{
HE* he;
HE* heend;
- New(54, he, PERL_ARENA_SIZE/sizeof(HE), HE);
+ Newx(he, PERL_ARENA_SIZE/sizeof(HE), HE);
HeNEXT(he) = PL_he_arenaroot;
PL_he_arenaroot = he;
HeNEXT(he) = 0;
}
+#ifdef PURIFY
+
+#define new_HE() (HE*)safemalloc(sizeof(HE))
+#define del_HE(p) safefree((char*)p)
+
+#else
+
STATIC HE*
S_new_he(pTHX)
{
return he;
}
-STATIC void
-S_del_he(pTHX_ HE *p)
-{
- LOCK_SV_MUTEX;
- HeNEXT(p) = (HE*)PL_he_root;
- PL_he_root = p;
- UNLOCK_SV_MUTEX;
-}
-
-#ifdef PURIFY
+#define new_HE() new_he()
+#define del_HE(p) \
+ STMT_START { \
+ LOCK_SV_MUTEX; \
+ HeNEXT(p) = (HE*)PL_he_root; \
+ PL_he_root = p; \
+ UNLOCK_SV_MUTEX; \
+ } STMT_END
-#define new_HE() (HE*)safemalloc(sizeof(HE))
-#define del_HE(p) safefree((char*)p)
-#else
-
-#define new_HE() new_he()
-#define del_HE(p) del_he(p)
#endif
char *k;
register HEK *hek;
- New(54, k, HEK_BASESIZE + len + 2, char);
+ Newx(k, HEK_BASESIZE + len + 2, char);
hek = (HEK*)k;
Copy(str, HEK_KEY(hek), len, char);
HEK_KEY(hek)[len] = 0;
void
Perl_free_tied_hv_pool(pTHX)
{
- HE *ohe;
HE *he = PL_hv_fetch_ent_mh;
while (he) {
+ HE * const ohe = he;
Safefree(HeKEY_hek(he));
- ohe = he;
he = HeNEXT(he);
del_HE(ohe);
}
HEK *
Perl_hek_dup(pTHX_ HEK *source, CLONE_PARAMS* param)
{
- HE *shared = (HE*)ptr_table_fetch(PL_shared_hek_table, source);
+ HEK *shared = (HEK*)ptr_table_fetch(PL_ptr_table, source);
+
+ PERL_UNUSED_ARG(param);
if (shared) {
/* We already shared this hash key. */
- ++HeVAL(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_shared_hek_table, source, shared);
+ 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 HeKEY_hek(shared);
+ return shared;
}
HE *
HeNEXT(ret) = he_dup(HeNEXT(e),shared, param);
if (HeKLEN(e) == HEf_SVKEY) {
char *k;
- New(54, k, HEK_BASESIZE + sizeof(SV*), char);
+ Newx(k, HEK_BASESIZE + sizeof(SV*), char);
HeKEY_hek(ret) = (HEK*)k;
HeKEY_sv(ret) = SvREFCNT_inc(sv_dup(HeKEY_sv(e), param));
}
else if (shared) {
/* This is hek_dup inlined, which seems to be important for speed
reasons. */
- HEK *source = HeKEY_hek(e);
- HE *shared = (HE*)ptr_table_fetch(PL_shared_hek_table, source);
+ HEK * const source = HeKEY_hek(e);
+ HEK *shared = (HEK*)ptr_table_fetch(PL_ptr_table, source);
if (shared) {
/* We already shared this hash key. */
- ++HeVAL(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_shared_hek_table, source, shared);
+ 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) = HeKEY_hek(shared);
+ HeKEY_hek(ret) = shared;
}
else
HeKEY_hek(ret) = save_hek_flags(HeKEY(e), HeKLEN(e), HeHASH(e),
S_hv_notallowed(pTHX_ int flags, const char *key, I32 klen,
const char *msg)
{
- SV *sv = sv_newmortal();
+ SV * const sv = sv_newmortal();
if (!(flags & HVhek_FREEKEY)) {
sv_setpvn(sv, key, klen);
}
Perl_hv_store_flags(pTHX_ HV *hv, const char *key, I32 klen, SV *val,
register U32 hash, int flags)
{
- HE *hek = hv_fetch_common (hv, NULL, key, klen, flags,
+ HE * const hek = hv_fetch_common (hv, NULL, key, klen, flags,
(HV_FETCH_ISSTORE|HV_FETCH_JUST_SV), val, hash);
return hek ? &HeVAL(hek) : NULL;
}
{
dVAR;
XPVHV* xhv;
- U32 n_links;
HE *entry;
HE **oentry;
SV *sv;
if (keysv) {
if (flags & HVhek_FREEKEY)
Safefree(key);
- key = SvPV(keysv, klen);
+ key = SvPV_const(keysv, klen);
flags = 0;
is_utf8 = (SvUTF8(keysv) != 0);
} else {
else {
char *k;
entry = new_HE();
- New(54, k, HEK_BASESIZE + sizeof(SV*), char);
+ Newx(k, HEK_BASESIZE + sizeof(SV*), char);
HeKEY_hek(entry) = (HEK*)k;
}
HeNEXT(entry) = Nullhe;
} /* ISFETCH */
else if (SvRMAGICAL(hv) && (action & HV_FETCH_ISEXISTS)) {
if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) {
- SV* svret;
/* I don't understand why hv_exists_ent has svret and sv,
whereas hv_exists only had one. */
- svret = sv_newmortal();
+ SV * const svret = sv_newmortal();
sv = sv_newmortal();
if (keysv || is_utf8) {
#ifdef ENV_IS_CASELESS
else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
/* XXX This code isn't UTF8 clean. */
- const char *keysave = key;
+ char * const keysave = (char * const)key;
/* Will need to free this, so set FREEKEY flag. */
key = savepvn(key,klen);
key = (const char*)strupr((char*)key);
#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, HvARRAY(hv),
+ ) {
+ char *array;
+ Newxz(array,
PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
- HE*);
+ char);
+ HvARRAY(hv) = (HE**)array;
+ }
#ifdef DYNAMIC_ENV_FETCH
else if (action & HV_FETCH_ISEXISTS) {
/* for an %ENV exists, if we do an insert it's by a recursive
}
if (is_utf8) {
- const char *keysave = key;
+ char * const keysave = (char * const)key;
key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8);
if (is_utf8)
flags |= HVhek_UTF8;
flags |= HVhek_REHASH;
} else if (!hash) {
if (keysv && (SvIsCOW_shared_hash(keysv))) {
- hash = SvUVX(keysv);
+ hash = SvSHARED_HASH(keysv);
} else {
PERL_HASH(hash, key, klen);
}
}
masked_flags = (flags & HVhek_MASK);
- n_links = 0;
#ifdef DYNAMIC_ENV_FETCH
if (!HvARRAY(hv)) entry = Null(HE*);
{
entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)];
}
- for (; entry; ++n_links, entry = HeNEXT(entry)) {
+ for (; entry; entry = HeNEXT(entry)) {
if (HeHASH(entry) != hash) /* strings can't be equal */
continue;
if (HeKLEN(entry) != (I32)klen)
/* Need to swap the key we have for a key with the flags we
need. As keys are shared we can't just write to the
flag, so we share the new one, unshare the old one. */
- HEK *new_hek = HeKEY_hek(share_hek_flags(key, klen, hash,
- masked_flags));
+ HEK *new_hek = share_hek_flags(key, klen, hash,
+ masked_flags);
unshare_hek (HeKEY_hek(entry));
HeKEY_hek(entry) = new_hek;
}
+ else if (hv == PL_strtab) {
+ /* PL_strtab is usually the only hash without HvSHAREKEYS,
+ so putting this test here is cheap */
+ if (flags & HVhek_FREEKEY)
+ Safefree(key);
+ Perl_croak(aTHX_ S_strtab_error,
+ action & HV_FETCH_LVALUE ? "fetch" : "store");
+ }
else
HeKFLAGS(entry) = masked_flags;
if (masked_flags & HVhek_ENABLEHVKFLAGS)
if (!(action & HV_FETCH_ISSTORE)
&& SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) {
unsigned long len;
- char *env = PerlEnv_ENVgetenv_len(key,&len);
+ const char * const env = PerlEnv_ENVgetenv_len(key,&len);
if (env) {
sv = newSVpvn(env,len);
SvTAINTED_on(sv);
/* 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, HvARRAY(hv),
+ char *array;
+ Newxz(array,
PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
- HE*);
+ char);
+ HvARRAY(hv) = (HE**)array;
}
oentry = &(HvARRAY(hv))[hash & (I32) xhv->xhv_max];
/* share_hek_flags will do the free for us. This might be considered
bad API design. */
if (HvSHAREKEYS(hv))
- HeKEY_hek(entry) = HeKEY_hek(share_hek_flags(key, klen, hash, flags));
+ HeKEY_hek(entry) = share_hek_flags(key, klen, hash, flags);
+ else if (hv == PL_strtab) {
+ /* PL_strtab is usually the only hash without HvSHAREKEYS, so putting
+ this test here is cheap */
+ if (flags & HVhek_FREEKEY)
+ Safefree(key);
+ Perl_croak(aTHX_ S_strtab_error,
+ action & HV_FETCH_LVALUE ? "fetch" : "store");
+ }
else /* gotta do the real thing */
HeKEY_hek(entry) = save_hek_flags(key, klen, hash, flags);
HeVAL(entry) = val;
if (masked_flags & HVhek_ENABLEHVKFLAGS)
HvHASKFLAGS_on(hv);
- xhv->xhv_keys++; /* HvKEYS(hv)++ */
- if (!n_links) { /* initial entry? */
- xhv->xhv_fill++; /* HvFILL(hv)++ */
- } else if ((xhv->xhv_keys > (IV)xhv->xhv_max)
- || ((n_links > HV_MAX_LENGTH_BEFORE_SPLIT) && !HvREHASH(hv))) {
- /* Use only the old HvKEYS(hv) > HvMAX(hv) condition to limit bucket
- splits on a rehashed hash, as we're not going to split it again,
- and if someone is lucky (evil) enough to get all the keys in one
- list they could exhaust our memory as we repeatedly double the
- number of buckets on every entry. Linear search feels a less worse
- thing to do. */
- hsplit(hv);
+ {
+ const HE *counter = HeNEXT(entry);
+
+ xhv->xhv_keys++; /* HvKEYS(hv)++ */
+ if (!counter) { /* initial entry? */
+ xhv->xhv_fill++; /* HvFILL(hv)++ */
+ } else if (xhv->xhv_keys > (IV)xhv->xhv_max) {
+ hsplit(hv);
+ } else if(!HvREHASH(hv)) {
+ U32 n_links = 1;
+
+ while ((counter = HeNEXT(counter)))
+ n_links++;
+
+ if (n_links > HV_MAX_LENGTH_BEFORE_SPLIT) {
+ /* Use only the old HvKEYS(hv) > HvMAX(hv) condition to limit
+ bucket splits on a rehashed hash, as we're not going to
+ split it again, and if someone is lucky (evil) enough to
+ get all the keys in one list they could exhaust our memory
+ as we repeatedly double the number of buckets on every
+ entry. Linear search feels a less worse thing to do. */
+ hsplit(hv);
+ }
+ }
}
return entry;
{
dVAR;
register XPVHV* xhv;
- register I32 i;
register HE *entry;
register HE **oentry;
+ HE *const *first_entry;
SV *sv;
bool is_utf8;
int masked_flags;
if (keysv) {
if (k_flags & HVhek_FREEKEY)
Safefree(key);
- key = SvPV(keysv, klen);
+ key = SvPV_const(keysv, klen);
k_flags = 0;
is_utf8 = (SvUTF8(keysv) != 0);
} else {
return Nullsv;
if (is_utf8) {
- const char *keysave = key;
- key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8);
+ const char *keysave = key;
+ key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8);
if (is_utf8)
k_flags |= HVhek_UTF8;
PERL_HASH_INTERNAL(hash, key, klen);
} else if (!hash) {
if (keysv && (SvIsCOW_shared_hash(keysv))) {
- hash = SvUVX(keysv);
+ hash = SvSHARED_HASH(keysv);
} else {
PERL_HASH(hash, key, klen);
}
masked_flags = (k_flags & HVhek_MASK);
- oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)];
+ first_entry = oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)];
entry = *oentry;
- i = 1;
- for (; entry; i=0, oentry = &HeNEXT(entry), entry = *oentry) {
+ for (; entry; oentry = &HeNEXT(entry), entry = *oentry) {
if (HeHASH(entry) != hash) /* strings can't be equal */
continue;
if (HeKLEN(entry) != (I32)klen)
if ((HeKFLAGS(entry) ^ masked_flags) & HVhek_UTF8)
continue;
+ if (hv == PL_strtab) {
+ if (k_flags & HVhek_FREEKEY)
+ Safefree(key);
+ Perl_croak(aTHX_ S_strtab_error, "delete");
+ }
+
/* if placeholder is here, it's already been deleted.... */
if (HeVAL(entry) == &PL_sv_placeholder)
{
HvPLACEHOLDERS(hv)++;
} else {
*oentry = HeNEXT(entry);
- if (i && !*oentry)
+ if(!*first_entry) {
xhv->xhv_fill--; /* HvFILL(hv)-- */
- if (xhv->xhv_aux && entry
- == ((struct xpvhv_aux *)xhv->xhv_aux)->xhv_eiter /* HvEITER(hv) */)
+ }
+ if (SvOOK(hv) && entry == HvAUX(hv)->xhv_eiter /* HvEITER(hv) */)
HvLAZYDEL_on(hv);
else
hv_free_ent(hv, entry);
PL_nomemok = TRUE;
#if defined(STRANGE_MALLOC) || defined(MYMALLOC)
- Renew(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
+ Renew(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize)
+ + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char);
if (!a) {
PL_nomemok = FALSE;
return;
}
+ if (SvOOK(hv)) {
+ Copy(&a[oldsize * sizeof(HE*)], &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux);
+ }
#else
- New(2, a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
+ Newx(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize)
+ + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char);
if (!a) {
PL_nomemok = FALSE;
return;
}
Copy(HvARRAY(hv), a, oldsize * sizeof(HE*), char);
+ if (SvOOK(hv)) {
+ Copy(HvAUX(hv), &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux);
+ }
if (oldsize >= 64) {
offer_nice_chunk(HvARRAY(hv),
- PERL_HV_ARRAY_ALLOC_BYTES(oldsize));
+ PERL_HV_ARRAY_ALLOC_BYTES(oldsize)
+ + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0));
}
else
Safefree(HvARRAY(hv));
longest_chain, HvTOTALKEYS(hv), HvFILL(hv), 1+HvMAX(hv));*/
++newsize;
- Newz(2, a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
+ Newxz(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize)
+ + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char);
+ if (SvOOK(hv)) {
+ Copy(HvAUX(hv), &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux);
+ }
+
was_shared = HvSHAREKEYS(hv);
xhv->xhv_fill = 0;
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);
+ HE * const next = HeNEXT(entry);
UV hash;
HE **bep;
if (was_shared) {
/* Unshare it. */
- HEK *new_hek
+ HEK * const new_hek
= save_hek_flags(HeKEY(entry), HeKLEN(entry),
hash, HeKFLAGS(entry));
unshare_hek (HeKEY_hek(entry));
if (a) {
PL_nomemok = TRUE;
#if defined(STRANGE_MALLOC) || defined(MYMALLOC)
- Renew(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
+ Renew(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize)
+ + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char);
if (!a) {
PL_nomemok = FALSE;
return;
}
+ if (SvOOK(hv)) {
+ Copy(&a[oldsize * sizeof(HE*)], &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux);
+ }
#else
- New(2, a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
+ Newx(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize)
+ + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char);
if (!a) {
PL_nomemok = FALSE;
return;
}
Copy(HvARRAY(hv), a, oldsize * sizeof(HE*), char);
+ if (SvOOK(hv)) {
+ Copy(HvAUX(hv), &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux);
+ }
if (oldsize >= 64) {
offer_nice_chunk(HvARRAY(hv),
- PERL_HV_ARRAY_ALLOC_BYTES(oldsize));
+ PERL_HV_ARRAY_ALLOC_BYTES(oldsize)
+ + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0));
}
else
Safefree(HvARRAY(hv));
Zero(&a[oldsize * sizeof(HE*)], (newsize-oldsize) * sizeof(HE*), char); /* zero 2nd half*/
}
else {
- Newz(0, a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
+ Newxz(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
}
xhv->xhv_max = --newsize; /* HvMAX(hv) = --newsize */
HvARRAY(hv) = (HE **) a;
HV *
Perl_newHV(pTHX)
{
- register HV *hv;
register XPVHV* xhv;
+ HV * const hv = (HV*)NEWSV(502,0);
- hv = (HV*)NEWSV(502,0);
sv_upgrade((SV *)hv, SVt_PVHV);
xhv = (XPVHV*)SvANY(hv);
SvPOK_off(hv);
xhv->xhv_max = 7; /* HvMAX(hv) = 7 (start with 8 buckets) */
xhv->xhv_fill = 0; /* HvFILL(hv) = 0 */
- xhv->xhv_aux = 0;
return hv;
}
HV *
Perl_newHVhv(pTHX_ HV *ohv)
{
- HV *hv = newHV();
+ HV * const hv = newHV();
STRLEN hv_max, hv_fill;
if (!ohv || (hv_fill = HvFILL(ohv)) == 0)
/* It's an ordinary hash, so copy it fast. AMS 20010804 */
STRLEN i;
const bool shared = !!HvSHAREKEYS(ohv);
- HE **ents, **oents = (HE **)HvARRAY(ohv);
+ HE **ents, ** const oents = (HE **)HvARRAY(ohv);
char *a;
- New(0, a, PERL_HV_ARRAY_ALLOC_BYTES(hv_max+1), char);
+ Newx(a, PERL_HV_ARRAY_ALLOC_BYTES(hv_max+1), char);
ents = (HE**)a;
/* In each bucket... */
for (i = 0; i <= hv_max; i++) {
- HE *prev = NULL, *ent = NULL, *oent = oents[i];
+ HE *prev = NULL, *ent = NULL;
+ HE *oent = oents[i];
if (!oent) {
ents[i] = NULL;
}
/* Copy the linked list of entries. */
- for (oent = oents[i]; oent; oent = HeNEXT(oent)) {
+ for (; oent; oent = HeNEXT(oent)) {
const U32 hash = HeHASH(oent);
const char * const key = HeKEY(oent);
const STRLEN len = HeKLEN(oent);
ent = new_HE();
HeVAL(ent) = newSVsv(HeVAL(oent));
HeKEY_hek(ent)
- = shared ? HeKEY_hek(share_hek_flags(key, len, hash, flags))
+ = shared ? share_hek_flags(key, len, hash, flags)
: save_hek_flags(key, len, hash, flags);
if (prev)
HeNEXT(prev) = ent;
HvFILL(hv) = hv_fill;
HvTOTALKEYS(hv) = HvTOTALKEYS(ohv);
HvARRAY(hv) = ents;
- }
+ } /* not magical */
else {
/* Iterate over ohv, copying keys and values one at a time. */
HE *entry;
{
if (!entry)
return;
- 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 */
+ /* SvREFCNT_inc to counter the SvREFCNT_dec in hv_free_ent */
+ sv_2mortal(SvREFCNT_inc(HeVAL(entry))); /* free between statements */
if (HeKLEN(entry) == HEf_SVKEY) {
- sv_2mortal(HeKEY_sv(entry));
- Safefree(HeKEY_hek(entry));
+ sv_2mortal(SvREFCNT_inc(HeKEY_sv(entry)));
}
- else if (HvSHAREKEYS(hv))
- unshare_hek(HeKEY_hek(entry));
- else
- Safefree(HeKEY_hek(entry));
- del_HE(entry);
+ hv_free_ent(hv, entry);
}
/*
if (SvREADONLY(hv) && HvARRAY(hv) != NULL) {
/* restricted hash: convert all keys to placeholders */
- I32 i;
- for (i = 0; i <= (I32) xhv->xhv_max; i++) {
+ STRLEN i;
+ for (i = 0; i <= xhv->xhv_max; i++) {
HE *entry = (HvARRAY(hv))[i];
for (; entry; entry = HeNEXT(entry)) {
/* not already placeholder */
HvHASKFLAGS_off(hv);
HvREHASH_off(hv);
reset:
- if (xhv->xhv_aux) {
+ if (SvOOK(hv)) {
HvEITER_set(hv, NULL);
}
}
{
dVAR;
I32 items = (I32)HvPLACEHOLDERS_get(hv);
- I32 i = HvMAX(hv);
+ I32 i;
if (items == 0)
return;
+ i = HvMAX(hv);
do {
/* Loop down the linked list heads */
bool first = 1;
I32 max;
struct xpvhv_aux *iter;
- if (!hv)
- return;
if (!HvARRAY(hv))
return;
+ iter = SvOOK(hv) ? HvAUX(hv) : 0;
+
riter = 0;
max = HvMAX(hv);
array = HvARRAY(hv);
/* make everyone else think the array is empty, so that the destructors
* called for freed entries can't recusively mess with us */
HvARRAY(hv) = Null(HE**);
+ SvFLAGS(hv) &= ~SVf_OOK;
+
HvFILL(hv) = 0;
((XPVHV*) SvANY(hv))->xhv_keys = 0;
entry = array[0];
for (;;) {
if (entry) {
- register HE *oentry = entry;
+ register HE * const oentry = entry;
entry = HeNEXT(entry);
hv_free_ent(hv, oentry);
}
entry = array[riter];
}
}
- HvARRAY(hv) = array;
- iter = ((XPVHV*) SvANY(hv))->xhv_aux;
+ if (SvOOK(hv)) {
+ /* Someone attempted to iterate or set the hash name while we had
+ the array set to 0. */
+ assert(HvARRAY(hv));
+
+ if (HvAUX(hv)->xhv_name)
+ unshare_hek_or_pvn(HvAUX(hv)->xhv_name, 0, 0, 0);
+ /* SvOOK_off calls sv_backoff, which isn't correct. */
+
+ Safefree(HvARRAY(hv));
+ HvARRAY(hv) = 0;
+ SvFLAGS(hv) &= ~SVf_OOK;
+ }
+
+ /* FIXME - things will still go horribly wrong (or at least leak) if
+ people attempt to add elements to the hash while we're undef()ing it */
if (iter) {
entry = iter->xhv_eiter; /* HvEITER(hv) */
if (entry && HvLAZYDEL(hv)) { /* was deleted earlier? */
HvLAZYDEL_off(hv);
hv_free_ent(hv, entry);
}
- if (iter->xhv_name)
- unshare_hek_or_pvn(iter->xhv_name, 0, 0, 0);
- Safefree(iter);
- ((XPVHV*) SvANY(hv))->xhv_aux = 0;
+ iter->xhv_riter = -1; /* HvRITER(hv) = -1 */
+ iter->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */
+ SvFLAGS(hv) |= SVf_OOK;
}
+
+ HvARRAY(hv) = array;
}
/*
DEBUG_A(Perl_hv_assert(aTHX_ hv));
xhv = (XPVHV*)SvANY(hv);
hfreeentries(hv);
- Safefree(HvARRAY(hv));
if ((name = HvNAME_get(hv))) {
if(PL_stashcache)
hv_delete(PL_stashcache, name, HvNAMELEN_get(hv), G_DISCARD);
- Perl_hv_name_set(aTHX_ hv, 0, 0, 0);
+ Perl_hv_name_set(aTHX_ hv, Nullch, 0, 0);
}
+ SvFLAGS(hv) &= ~SVf_OOK;
+ Safefree(HvARRAY(hv));
xhv->xhv_max = 7; /* HvMAX(hv) = 7 (it's a normal hash) */
HvARRAY(hv) = 0;
HvPLACEHOLDERS_set(hv, 0);
mg_clear((SV*)hv);
}
-struct xpvhv_aux*
-S_hv_auxinit(pTHX) {
+static struct xpvhv_aux*
+S_hv_auxinit(pTHX_ HV *hv) {
struct xpvhv_aux *iter;
+ char *array;
- New(0, iter, 1, struct xpvhv_aux);
+ if (!HvARRAY(hv)) {
+ Newxz(array, PERL_HV_ARRAY_ALLOC_BYTES(HvMAX(hv) + 1)
+ + sizeof(struct xpvhv_aux), char);
+ } else {
+ array = (char *) HvARRAY(hv);
+ Renew(array, PERL_HV_ARRAY_ALLOC_BYTES(HvMAX(hv) + 1)
+ + sizeof(struct xpvhv_aux), char);
+ }
+ HvARRAY(hv) = (HE**) array;
+ /* SvOOK_on(hv) attacks the IV flags. */
+ SvFLAGS(hv) |= SVf_OOK;
+ iter = HvAUX(hv);
iter->xhv_riter = -1; /* HvRITER(hv) = -1 */
iter->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */
I32
Perl_hv_iterinit(pTHX_ HV *hv)
{
- register XPVHV* xhv;
HE *entry;
- struct xpvhv_aux *iter;
if (!hv)
Perl_croak(aTHX_ "Bad hash");
- xhv = (XPVHV*)SvANY(hv);
- iter = xhv->xhv_aux;
- if (iter) {
+ if (SvOOK(hv)) {
+ struct xpvhv_aux *iter = HvAUX(hv);
entry = iter->xhv_eiter; /* HvEITER(hv) */
if (entry && HvLAZYDEL(hv)) { /* was deleted earlier? */
HvLAZYDEL_off(hv);
iter->xhv_riter = -1; /* HvRITER(hv) = -1 */
iter->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */
} else {
- xhv->xhv_aux = S_hv_auxinit(aTHX);
+ S_hv_auxinit(aTHX_ hv);
}
/* used to be xhv->xhv_fill before 5.004_65 */
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);
- }
+ iter = SvOOK(hv) ? HvAUX(hv) : S_hv_auxinit(aTHX_ hv);
return &(iter->xhv_riter);
}
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);
- }
+ iter = SvOOK(hv) ? HvAUX(hv) : S_hv_auxinit(aTHX_ hv);
return &(iter->xhv_eiter);
}
if (!hv)
Perl_croak(aTHX_ "Bad hash");
-
- iter = ((XPVHV *)SvANY(hv))->xhv_aux;
- if (!iter) {
+ if (SvOOK(hv)) {
+ iter = HvAUX(hv);
+ } else {
if (riter == -1)
return;
- ((XPVHV *)SvANY(hv))->xhv_aux = iter = S_hv_auxinit(aTHX);
+ iter = S_hv_auxinit(aTHX_ hv);
}
iter->xhv_riter = riter;
}
if (!hv)
Perl_croak(aTHX_ "Bad hash");
- iter = ((XPVHV *)SvANY(hv))->xhv_aux;
- if (!iter) {
+ if (SvOOK(hv)) {
+ iter = HvAUX(hv);
+ } else {
/* 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 = S_hv_auxinit(aTHX_ hv);
}
iter->xhv_eiter = eiter;
}
void
Perl_hv_name_set(pTHX_ HV *hv, const char *name, I32 len, int flags)
{
- struct xpvhv_aux *iter = ((XPVHV *)SvANY(hv))->xhv_aux;
+ struct xpvhv_aux *iter;
U32 hash;
- if (iter) {
+ PERL_UNUSED_ARG(flags);
+
+ if (SvOOK(hv)) {
+ iter = HvAUX(hv);
if (iter->xhv_name) {
unshare_hek_or_pvn(iter->xhv_name, 0, 0, 0);
}
if (name == 0)
return;
- ((XPVHV *)SvANY(hv))->xhv_aux = iter = S_hv_auxinit(aTHX);
+ iter = S_hv_auxinit(aTHX_ hv);
}
PERL_HASH(hash, name, len);
iter->xhv_name = name ? share_hek(name, len, hash) : 0;
if (!hv)
Perl_croak(aTHX_ "Bad hash");
xhv = (XPVHV*)SvANY(hv);
- iter = xhv->xhv_aux;
- if (!iter) {
+ if (!SvOOK(hv)) {
/* 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;
}
+ iter = HvAUX(hv);
oldentry = entry = iter->xhv_eiter; /* HvEITER(hv) */
/* one HE per MAGICAL hash */
iter->xhv_eiter = entry = new_HE(); /* HvEITER(hv) = new_HE() */
Zero(entry, 1, HE);
- Newz(54, k, HEK_BASESIZE + sizeof(SV*), char);
+ Newxz(k, HEK_BASESIZE + sizeof(SV*), char);
hek = (HEK*)k;
HeKEY_hek(entry) = hek;
HeKLEN(entry) = HEf_SVKEY;
prime_env_iter();
#endif
- 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;
- }
+ /* hv_iterint now ensures this. */
+ assert (HvARRAY(hv));
+
/* At start of hash, entry is NULL. */
if (entry)
{
SV *
Perl_hv_iterkeysv(pTHX_ register HE *entry)
{
- if (HeKLEN(entry) != HEf_SVKEY) {
- HEK *hek = HeKEY_hek(entry);
- const int flags = HEK_FLAGS(hek);
- SV *sv;
-
- if (flags & HVhek_WASUTF8) {
- /* Trouble :-)
- Andreas would like keys he put in as utf8 to come back as utf8
- */
- STRLEN utf8_len = HEK_LEN(hek);
- U8 *as_utf8 = bytes_to_utf8 ((U8*)HEK_KEY(hek), &utf8_len);
-
- sv = newSVpvn ((char*)as_utf8, utf8_len);
- SvUTF8_on (sv);
- Safefree (as_utf8); /* bytes_to_utf8() allocates a new string */
- } else if (flags & HVhek_REHASH) {
- /* We don't have a pointer to the hv, so we have to replicate the
- flag into every HEK. This hv is using custom a hasing
- algorithm. Hence we can't return a shared string scalar, as
- that would contain the (wrong) hash value, and might get passed
- into an hv routine with a regular hash */
-
- sv = newSVpvn (HEK_KEY(hek), HEK_LEN(hek));
- if (HEK_UTF8(hek))
- SvUTF8_on (sv);
- } else {
- sv = newSVpvn_share(HEK_KEY(hek),
- (HEK_UTF8(hek) ? -HEK_LEN(hek) : HEK_LEN(hek)),
- HEK_HASH(hek));
- }
- return sv_2mortal(sv);
- }
- return sv_mortalcopy(HeKEY_sv(entry));
+ return sv_2mortal(newSVhek(HeKEY_hek(entry)));
}
/*
are used. If so, len and hash must both be valid for str.
*/
STATIC void
-S_unshare_hek_or_pvn(pTHX_ HEK *hek, const char *str, I32 len, U32 hash)
+S_unshare_hek_or_pvn(pTHX_ const HEK *hek, const char *str, I32 len, U32 hash)
{
register XPVHV* xhv;
register HE *entry;
register HE **oentry;
- register I32 i = 1;
+ HE **first;
bool found = 0;
bool is_utf8 = FALSE;
int k_flags = 0;
- const char *save = str;
+ const char * const save = str;
+ struct shared_he *he = 0;
if (hek) {
+ /* Find the shared he which is just before us in memory. */
+ he = (struct shared_he *)(((char *)hek)
+ - STRUCT_OFFSET(struct shared_he,
+ shared_he_hek));
+
+ /* Assert that the caller passed us a genuine (or at least consistent)
+ shared hek */
+ assert (he->shared_he_he.hent_hek == hek);
+
+ LOCK_STRTAB_MUTEX;
+ if (he->shared_he_he.hent_val - 1) {
+ --he->shared_he_he.hent_val;
+ UNLOCK_STRTAB_MUTEX;
+ return;
+ }
+ UNLOCK_STRTAB_MUTEX;
+
hash = HEK_HASH(hek);
} else if (len < 0) {
STRLEN tmplen = -len;
xhv = (XPVHV*)SvANY(PL_strtab);
/* assert(xhv_array != 0) */
LOCK_STRTAB_MUTEX;
- 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)
+ first = 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)
continue;
found = 1;
break;
}
} else {
const int flags_masked = k_flags & HVhek_MASK;
- for (entry = *oentry; entry; i=0, oentry = &HeNEXT(entry), entry = *oentry) {
+ for (entry = *oentry; entry; oentry = &HeNEXT(entry), entry = *oentry) {
if (HeHASH(entry) != hash) /* strings can't be equal */
continue;
if (HeKLEN(entry) != len)
if (found) {
if (--HeVAL(entry) == Nullsv) {
*oentry = HeNEXT(entry);
- if (i && !*oentry)
+ if (!*first) {
+ /* There are now no entries in our slot. */
xhv->xhv_fill--; /* HvFILL(hv)-- */
- Safefree(HeKEY_hek(entry));
- del_HE(entry);
+ }
+ Safefree(entry);
xhv->xhv_keys--; /* HvKEYS(hv)-- */
}
}
{
bool is_utf8 = FALSE;
int flags = 0;
- const char *save = str;
+ const char * const save = str;
if (len < 0) {
STRLEN tmplen = -len;
flags |= HVhek_WASUTF8 | HVhek_FREEKEY;
}
- return HeKEY_hek(share_hek_flags (str, len, hash, flags));
+ return share_hek_flags (str, len, hash, flags);
}
-STATIC HE *
+STATIC HEK *
S_share_hek_flags(pTHX_ const char *str, I32 len, register U32 hash, int flags)
{
- register XPVHV* xhv;
register HE *entry;
register HE **oentry;
- register I32 i = 1;
I32 found = 0;
const int flags_masked = flags & HVhek_MASK;
Can't rehash the shared string table, so not sure if it's worth
counting the number of entries in the linked list
*/
- xhv = (XPVHV*)SvANY(PL_strtab);
+ register XPVHV * const xhv = (XPVHV*)SvANY(PL_strtab);
/* assert(xhv_array != 0) */
LOCK_STRTAB_MUTEX;
oentry = &(HvARRAY(PL_strtab))[hash & (I32) HvMAX(PL_strtab)];
- for (entry = *oentry; entry; i=0, entry = HeNEXT(entry)) {
+ for (entry = *oentry; entry; entry = HeNEXT(entry)) {
if (HeHASH(entry) != hash) /* strings can't be equal */
continue;
if (HeKLEN(entry) != len)
break;
}
if (!found) {
- entry = new_HE();
- HeKEY_hek(entry) = save_hek_flags(str, len, hash, flags_masked);
+ /* What used to be head of the list.
+ If this is NULL, then we're the first entry for this slot, which
+ means we need to increate fill. */
+ const HE *old_first = *oentry;
+ struct shared_he *new_entry;
+ HEK *hek;
+ char *k;
+
+ /* We don't actually store a HE from the arena and a regular HEK.
+ Instead we allocate one chunk of memory big enough for both,
+ and put the HEK straight after the HE. This way we can find the
+ HEK directly from the HE.
+ */
+
+ 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;
+
+ /* Still "point" to the HEK, so that other code need not know what
+ we're up to. */
+ HeKEY_hek(entry) = hek;
HeVAL(entry) = Nullsv;
HeNEXT(entry) = *oentry;
*oentry = entry;
+
xhv->xhv_keys++; /* HvKEYS(hv)++ */
- if (i) { /* initial entry? */
+ if (!old_first) { /* initial entry? */
xhv->xhv_fill++; /* HvFILL(hv)++ */
} else if (xhv->xhv_keys > (IV)xhv->xhv_max /* HvKEYS(hv) > HvMAX(hv) */) {
hsplit(PL_strtab);
if (flags & HVhek_FREEKEY)
Safefree(str);
- return entry;
+ return HeKEY_hek(entry);
}
I32 *
Perl_hv_placeholders_get(pTHX_ HV *hv)
{
dVAR;
- MAGIC *mg = mg_find((SV*)hv, PERL_MAGIC_rhash);
+ MAGIC * const mg = mg_find((SV*)hv, PERL_MAGIC_rhash);
return mg ? mg->mg_len : 0;
}
Perl_hv_placeholders_set(pTHX_ HV *hv, I32 ph)
{
dVAR;
- MAGIC *mg = mg_find((SV*)hv, PERL_MAGIC_rhash);
+ MAGIC * const mg = mg_find((SV*)hv, PERL_MAGIC_rhash);
if (mg) {
mg->mg_len = ph;