/* 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 PERL_IN_HV_C
+#define PERL_HASH_INTERNAL_ACCESS
#include "perl.h"
+#define HV_MAX_LENGTH_BEFORE_SPLIT 14
+
+static const char *const 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);
+ 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;
}
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);
}
+ PL_hv_fetch_ent_mh = Nullhe;
}
#if defined(USE_ITHREADS)
+HEK *
+Perl_hek_dup(pTHX_ HEK *source, CLONE_PARAMS* param)
+{
+ HEK *shared = (HEK*)ptr_table_fetch(PL_ptr_table, source);
+
+ PERL_UNUSED_ARG(param);
+
+ if (shared) {
+ /* We already shared this hash key. */
+ (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_ HE *e, bool shared, CLONE_PARAMS* param)
{
HeKEY_hek(ret) = (HEK*)k;
HeKEY_sv(ret) = SvREFCNT_inc(sv_dup(HeKEY_sv(e), param));
}
- else if (shared)
- HeKEY_hek(ret) = share_hek_flags(HeKEY(e), HeKLEN(e), HeHASH(e),
- HeKFLAGS(e));
+ else if (shared) {
+ /* This is hek_dup inlined, which seems to be important for speed
+ reasons. */
+ HEK * const source = HeKEY_hek(e);
+ HEK *shared = (HEK*)ptr_table_fetch(PL_ptr_table, source);
+
+ if (shared) {
+ /* We already shared this hash key. */
+ (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));
S_hv_notallowed(pTHX_ int flags, const char *key, I32 klen,
const char *msg)
{
- SV *sv = sv_newmortal(), *esv = sv_newmortal();
+ SV * const 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
* contains an SV* */
+#define HV_FETCH_ISSTORE 0x01
+#define HV_FETCH_ISEXISTS 0x02
+#define HV_FETCH_LVALUE 0x04
+#define HV_FETCH_JUST_SV 0x08
+
/*
-=for apidoc hv_fetch
+=for apidoc hv_store
-Returns the SV which corresponds to the specified key in the hash. The
-C<klen> is the length of the key. If C<lval> is set then the fetch will be
-part of a store. Check that the return value is non-null before
-dereferencing it to an C<SV*>.
+Stores an SV in a hash. The hash key is specified as C<key> and C<klen> is
+the length of the key. The C<hash> parameter is the precomputed hash
+value; if it is zero then Perl will compute it. The return value will be
+NULL if the operation failed or if the value did not need to be actually
+stored within the hash (as in the case of tied hashes). Otherwise it can
+be dereferenced to get the original C<SV*>. Note that the caller is
+responsible for suitably incrementing the reference count of C<val> before
+the call, and decrementing it if the function returned NULL. Effectively
+a successful hv_store takes ownership of one reference to C<val>. This is
+usually what you want; a newly created SV has a reference count of one, so
+if all your code does is create SVs then store them in a hash, hv_store
+will own the only reference to the new SV, and your code doesn't need to do
+anything further to tidy up. hv_store is not implemented as a call to
+hv_store_ent, and does not create a temporary SV for the key, so if your
+key data is not already in SV form then use hv_store in preference to
+hv_store_ent.
See L<perlguts/"Understanding the Magic of Tied Hashes and Arrays"> for more
information on how to use this function on tied hashes.
=cut
*/
-
SV**
-Perl_hv_fetch(pTHX_ HV *hv, const char *key, I32 klen, I32 lval)
+Perl_hv_store(pTHX_ HV *hv, const char *key, I32 klen_i32, SV *val, U32 hash)
{
- bool is_utf8 = FALSE;
- const char *keysave = key;
- int flags = 0;
-
- if (klen < 0) {
- klen = -klen;
- is_utf8 = TRUE;
- }
+ HE *hek;
+ STRLEN klen;
+ int flags;
- if (is_utf8) {
- STRLEN tmplen = klen;
- /* Just casting the &klen to (STRLEN) won't work well
- * if STRLEN and I32 are of different widths. --jhi */
- key = (char*)bytes_from_utf8((U8*)key, &tmplen, &is_utf8);
- klen = tmplen;
- /* If we were able to downgrade here, then than means that we were
- passed in a key which only had chars 0-255, but was utf8 encoded. */
- if (is_utf8)
- flags = HVhek_UTF8;
- /* If we found we were able to downgrade the string to bytes, then
- we should flag that it needs upgrading on keys or each. */
- if (key != keysave)
- flags |= HVhek_WASUTF8 | HVhek_FREEKEY;
+ if (klen_i32 < 0) {
+ klen = -klen_i32;
+ flags = HVhek_UTF8;
+ } else {
+ klen = klen_i32;
+ flags = 0;
}
+ hek = hv_fetch_common (hv, NULL, key, klen, flags,
+ (HV_FETCH_ISSTORE|HV_FETCH_JUST_SV), val, hash);
+ return hek ? &HeVAL(hek) : NULL;
+}
- return hv_fetch_flags (hv, key, klen, lval, flags);
+SV**
+Perl_hv_store_flags(pTHX_ HV *hv, const char *key, I32 klen, SV *val,
+ register U32 hash, int 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;
}
-STATIC SV**
-S_hv_fetch_flags(pTHX_ HV *hv, const char *key, I32 klen, I32 lval, int flags)
+/*
+=for apidoc hv_store_ent
+
+Stores C<val> in a hash. The hash key is specified as C<key>. The C<hash>
+parameter is the precomputed hash value; if it is zero then Perl will
+compute it. The return value is the new hash entry so created. It will be
+NULL if the operation failed or if the value did not need to be actually
+stored within the hash (as in the case of tied hashes). Otherwise the
+contents of the return value can be accessed using the C<He?> macros
+described here. Note that the caller is responsible for suitably
+incrementing the reference count of C<val> before the call, and
+decrementing it if the function returned NULL. Effectively a successful
+hv_store_ent takes ownership of one reference to C<val>. This is
+usually what you want; a newly created SV has a reference count of one, so
+if all your code does is create SVs then store them in a hash, hv_store
+will own the only reference to the new SV, and your code doesn't need to do
+anything further to tidy up. Note that hv_store_ent only reads the C<key>;
+unlike C<val> it does not take ownership of it, so maintaining the correct
+reference count on C<key> is entirely the caller's responsibility. hv_store
+is not implemented as a call to hv_store_ent, and does not create a temporary
+SV for the key, so if your key data is not already in SV form then use
+hv_store in preference to hv_store_ent.
+
+See L<perlguts/"Understanding the Magic of Tied Hashes and Arrays"> for more
+information on how to use this function on tied hashes.
+
+=cut
+*/
+
+HE *
+Perl_hv_store_ent(pTHX_ HV *hv, SV *keysv, SV *val, U32 hash)
{
- register XPVHV* xhv;
- register U32 hash;
- register HE *entry;
- SV *sv;
+ return hv_fetch_common(hv, keysv, NULL, 0, 0, HV_FETCH_ISSTORE, val, hash);
+}
- if (!hv)
- return 0;
+/*
+=for apidoc hv_exists
- if (SvRMAGICAL(hv)) {
- /* All this clause seems to be utf8 unaware.
- By moving the utf8 stuff out to hv_fetch_flags I need to ensure
- key doesn't leak. I've not tried solving the utf8-ness.
- NWC.
- */
- if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) {
- sv = sv_newmortal();
- sv_upgrade(sv, SVt_PVLV);
- mg_copy((SV*)hv, sv, key, klen);
- if (flags & HVhek_FREEKEY)
- Safefree(key);
- LvTYPE(sv) = 't';
- LvTARG(sv) = sv; /* fake (SV**) */
- return &(LvTARG(sv));
- }
-#ifdef ENV_IS_CASELESS
- else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
- I32 i;
- for (i = 0; i < klen; ++i)
- if (isLOWER(key[i])) {
- char *nkey = strupr(SvPVX(sv_2mortal(newSVpvn(key,klen))));
- SV **ret = hv_fetch(hv, nkey, klen, 0);
- if (!ret && lval) {
- ret = hv_store_flags(hv, key, klen, NEWSV(61,0), 0,
- flags);
- } else if (flags & HVhek_FREEKEY)
- Safefree(key);
- return ret;
- }
- }
-#endif
- }
+Returns a boolean indicating whether the specified hash key exists. The
+C<klen> is the length of the key.
- /* We use xhv->xhv_foo fields directly instead of HvFOO(hv) to
- avoid unnecessary pointer dereferencing. */
- xhv = (XPVHV*)SvANY(hv);
- if (!xhv->xhv_array /* !HvARRAY(hv) */) {
- if (lval
-#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) */,
- PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
- char);
- else {
- if (flags & HVhek_FREEKEY)
- Safefree(key);
- return 0;
- }
+=cut
+*/
+
+bool
+Perl_hv_exists(pTHX_ HV *hv, const char *key, I32 klen_i32)
+{
+ STRLEN klen;
+ int flags;
+
+ if (klen_i32 < 0) {
+ klen = -klen_i32;
+ flags = HVhek_UTF8;
+ } else {
+ klen = klen_i32;
+ flags = 0;
}
+ return hv_fetch_common(hv, NULL, key, klen, flags, HV_FETCH_ISEXISTS, 0, 0)
+ ? TRUE : FALSE;
+}
- PERL_HASH(hash, key, klen);
+/*
+=for apidoc hv_fetch
- /* entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
- entry = ((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
- for (; entry; entry = HeNEXT(entry)) {
- if (HeHASH(entry) != hash) /* strings can't be equal */
- continue;
- if (HeKLEN(entry) != (I32)klen)
- continue;
- if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen)) /* is this it? */
- continue;
- /* flags is 0 if not utf8. need HeKFLAGS(entry) also 0.
- flags is 1 if utf8. need HeKFLAGS(entry) also 1.
- xor is true if bits differ, in which case this isn't a match. */
- if ((HeKFLAGS(entry) ^ flags) & HVhek_UTF8)
- continue;
- if (lval && HeKFLAGS(entry) != flags) {
- /* We match if HVhek_UTF8 bit in our flags and hash key's match.
- But if entry was set previously with HVhek_WASUTF8 and key now
- doesn't (or vice versa) then we should change the key's flag,
- as this is assignment. */
- if (HvSHAREKEYS(hv)) {
- /* 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. */
- int flags_nofree = flags & ~HVhek_FREEKEY;
- HEK *new_hek = share_hek_flags(key, klen, hash, flags_nofree);
- unshare_hek (HeKEY_hek(entry));
- HeKEY_hek(entry) = new_hek;
- }
- else
- HeKFLAGS(entry) = flags;
- }
- if (flags & HVhek_FREEKEY)
- Safefree(key);
- /* if we find a placeholder, we pretend we haven't found anything */
- if (HeVAL(entry) == &PL_sv_undef)
- break;
- return &HeVAL(entry);
+Returns the SV which corresponds to the specified key in the hash. The
+C<klen> is the length of the key. If C<lval> is set then the fetch will be
+part of a store. Check that the return value is non-null before
+dereferencing it to an C<SV*>.
+See L<perlguts/"Understanding the Magic of Tied Hashes and Arrays"> for more
+information on how to use this function on tied hashes.
+
+=cut
+*/
+
+SV**
+Perl_hv_fetch(pTHX_ HV *hv, const char *key, I32 klen_i32, I32 lval)
+{
+ HE *hek;
+ STRLEN klen;
+ int flags;
+
+ if (klen_i32 < 0) {
+ klen = -klen_i32;
+ flags = HVhek_UTF8;
+ } else {
+ klen = klen_i32;
+ flags = 0;
}
-#ifdef DYNAMIC_ENV_FETCH /* %ENV lookup? If so, try to fetch the value now */
- if (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) {
- unsigned long len;
- char *env = PerlEnv_ENVgetenv_len(key,&len);
- if (env) {
- sv = newSVpvn(env,len);
- SvTAINTED_on(sv);
- if (flags & HVhek_FREEKEY)
- Safefree(key);
- return hv_store(hv,key,klen,sv,hash);
- }
- }
-#endif
- if (!entry && SvREADONLY(hv)) {
- S_hv_notallowed(aTHX_ flags, key, klen,
- "access disallowed key '%"SVf"' in"
- );
- }
- if (lval) { /* gonna assign to this, so it better be there */
- sv = NEWSV(61,0);
- return hv_store_flags(hv,key,klen,sv,hash,flags);
- }
- if (flags & HVhek_FREEKEY)
- Safefree(key);
- return 0;
+ hek = hv_fetch_common (hv, NULL, key, klen, flags,
+ HV_FETCH_JUST_SV | (lval ? HV_FETCH_LVALUE : 0),
+ Nullsv, 0);
+ return hek ? &HeVAL(hek) : NULL;
+}
+
+/*
+=for apidoc hv_exists_ent
+
+Returns a boolean indicating whether the specified hash key exists. C<hash>
+can be a valid precomputed hash value, or 0 to ask for it to be
+computed.
+
+=cut
+*/
+
+bool
+Perl_hv_exists_ent(pTHX_ HV *hv, SV *keysv, U32 hash)
+{
+ return hv_fetch_common(hv, keysv, NULL, 0, 0, HV_FETCH_ISEXISTS, 0, hash)
+ ? TRUE : FALSE;
}
/* returns an HE * structure with the all fields set */
HE *
Perl_hv_fetch_ent(pTHX_ HV *hv, SV *keysv, I32 lval, register U32 hash)
{
- register XPVHV* xhv;
- register char *key;
- STRLEN klen;
- register HE *entry;
+ return hv_fetch_common(hv, keysv, NULL, 0, 0,
+ (lval ? HV_FETCH_LVALUE : 0), Nullsv, hash);
+}
+
+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;
+ HE *entry;
+ HE **oentry;
SV *sv;
bool is_utf8;
- int flags = 0;
- char *keysave;
+ int masked_flags;
if (!hv)
return 0;
- if (SvRMAGICAL(hv)) {
- if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) {
- sv = sv_newmortal();
- keysv = newSVsv(keysv);
- mg_copy((SV*)hv, sv, (char*)keysv, HEf_SVKEY);
- /* grab a fake HE/HEK pair from the pool or make a new one */
- entry = PL_hv_fetch_ent_mh;
- if (entry)
- PL_hv_fetch_ent_mh = HeNEXT(entry);
- else {
- char *k;
- entry = new_HE();
- New(54, k, HEK_BASESIZE + sizeof(SV*), char);
- HeKEY_hek(entry) = (HEK*)k;
+ if (keysv) {
+ if (flags & HVhek_FREEKEY)
+ Safefree(key);
+ key = SvPV_const(keysv, klen);
+ flags = 0;
+ is_utf8 = (SvUTF8(keysv) != 0);
+ } else {
+ is_utf8 = ((flags & HVhek_UTF8) ? TRUE : FALSE);
+ }
+
+ xhv = (XPVHV*)SvANY(hv);
+ if (SvMAGICAL(hv)) {
+ if (SvRMAGICAL(hv) && !(action & (HV_FETCH_ISSTORE|HV_FETCH_ISEXISTS)))
+ {
+ if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) {
+ sv = sv_newmortal();
+
+ /* XXX should be able to skimp on the HE/HEK here when
+ HV_FETCH_JUST_SV is true. */
+
+ if (!keysv) {
+ keysv = newSVpvn(key, klen);
+ if (is_utf8) {
+ SvUTF8_on(keysv);
+ }
+ } else {
+ keysv = newSVsv(keysv);
+ }
+ mg_copy((SV*)hv, sv, (char *)keysv, HEf_SVKEY);
+
+ /* grab a fake HE/HEK pair from the pool or make a new one */
+ entry = PL_hv_fetch_ent_mh;
+ if (entry)
+ PL_hv_fetch_ent_mh = HeNEXT(entry);
+ else {
+ char *k;
+ entry = new_HE();
+ New(54, k, HEK_BASESIZE + sizeof(SV*), char);
+ HeKEY_hek(entry) = (HEK*)k;
+ }
+ HeNEXT(entry) = Nullhe;
+ HeSVKEY_set(entry, keysv);
+ HeVAL(entry) = sv;
+ sv_upgrade(sv, SVt_PVLV);
+ LvTYPE(sv) = 'T';
+ /* so we can free entry when freeing sv */
+ LvTARG(sv) = (SV*)entry;
+
+ /* XXX remove at some point? */
+ if (flags & HVhek_FREEKEY)
+ Safefree(key);
+
+ return entry;
}
- HeNEXT(entry) = Nullhe;
- HeSVKEY_set(entry, keysv);
- HeVAL(entry) = sv;
- sv_upgrade(sv, SVt_PVLV);
- LvTYPE(sv) = 'T';
- LvTARG(sv) = (SV*)entry; /* so we can free entry when freeing sv */
- return entry;
- }
#ifdef ENV_IS_CASELESS
- else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
- U32 i;
- key = SvPV(keysv, klen);
- for (i = 0; i < klen; ++i)
- if (isLOWER(key[i])) {
- SV *nkeysv = sv_2mortal(newSVpvn(key,klen));
- (void)strupr(SvPVX(nkeysv));
- entry = hv_fetch_ent(hv, nkeysv, 0, 0);
- if (!entry && lval)
- entry = hv_store_ent(hv, keysv, NEWSV(61,0), hash);
- return entry;
- }
- }
+ else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
+ U32 i;
+ for (i = 0; i < klen; ++i)
+ if (isLOWER(key[i])) {
+ /* 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
- }
+ } /* ISFETCH */
+ else if (SvRMAGICAL(hv) && (action & HV_FETCH_ISEXISTS)) {
+ if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) {
+ /* I don't understand why hv_exists_ent has svret and sv,
+ whereas hv_exists only had one. */
+ SV * const svret = sv_newmortal();
+ sv = sv_newmortal();
+
+ if (keysv || is_utf8) {
+ if (!keysv) {
+ keysv = newSVpvn(key, klen);
+ SvUTF8_on(keysv);
+ } else {
+ keysv = newSVsv(keysv);
+ }
+ mg_copy((SV*)hv, sv, (char *)sv_2mortal(keysv), HEf_SVKEY);
+ } else {
+ mg_copy((SV*)hv, sv, key, klen);
+ }
+ if (flags & HVhek_FREEKEY)
+ Safefree(key);
+ magic_existspack(svret, mg_find(sv, PERL_MAGIC_tiedelem));
+ /* This cast somewhat evil, but I'm merely using NULL/
+ not NULL to return the boolean exists.
+ And I know hv is not NULL. */
+ return SvTRUE(svret) ? (HE *)hv : NULL;
+ }
+#ifdef ENV_IS_CASELESS
+ else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
+ /* XXX This code isn't UTF8 clean. */
+ 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);
+ is_utf8 = 0;
+ hash = 0;
+ keysv = 0;
- xhv = (XPVHV*)SvANY(hv);
- if (!xhv->xhv_array /* !HvARRAY(hv) */) {
- if (lval
-#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))
+ if (flags & HVhek_FREEKEY) {
+ Safefree(keysave);
+ }
+ flags |= HVhek_FREEKEY;
+ }
#endif
- )
- Newz(503, xhv->xhv_array /* HvARRAY(hv) */,
- PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
- char);
- else
+ } /* ISEXISTS */
+ 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 = PL_tainted;
+ if (keysv || is_utf8) {
+ if (!keysv) {
+ keysv = newSVpvn(key, klen);
+ SvUTF8_on(keysv);
+ }
+ if (PL_tainting)
+ PL_tainted = SvTAINTED(keysv);
+ keysv = sv_2mortal(newSVsv(keysv));
+ mg_copy((SV*)hv, val, (char*)keysv, HEf_SVKEY);
+ } else {
+ mg_copy((SV*)hv, val, key, klen);
+ }
+
+ TAINT_IF(save_taint);
+ if (!HvARRAY(hv) && !needs_store) {
+ if (flags & HVhek_FREEKEY)
+ Safefree(key);
+ return Nullhe;
+ }
+#ifdef ENV_IS_CASELESS
+ else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
+ /* XXX This code isn't UTF8 clean. */
+ const char *keysave = key;
+ /* Will need to free this, so set FREEKEY flag. */
+ key = savepvn(key,klen);
+ key = (const char*)strupr((char*)key);
+ is_utf8 = 0;
+ hash = 0;
+ keysv = 0;
+
+ if (flags & HVhek_FREEKEY) {
+ Safefree(keysave);
+ }
+ flags |= HVhek_FREEKEY;
+ }
+#endif
+ }
+ } /* ISSTORE */
+ } /* SvMAGICAL */
+
+ 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
+ ) {
+ char *array;
+ Newz(503, array,
+ PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
+ 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
+ store call, so avoid creating HvARRAY(hv) right now. */
+ }
+#endif
+ else {
+ /* XXX remove at some point? */
+ if (flags & HVhek_FREEKEY)
+ Safefree(key);
+
return 0;
+ }
}
- keysave = key = SvPV(keysv, klen);
- is_utf8 = (SvUTF8(keysv)!=0);
-
if (is_utf8) {
+ char * const keysave = (char * const)key;
key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8);
if (is_utf8)
- flags = HVhek_UTF8;
- if (key != keysave)
+ flags |= HVhek_UTF8;
+ else
+ flags &= ~HVhek_UTF8;
+ if (key != keysave) {
+ if (flags & HVhek_FREEKEY)
+ Safefree(keysave);
flags |= HVhek_WASUTF8 | HVhek_FREEKEY;
+ }
}
- if (!hash) {
- if SvIsCOW_shared_hash(keysv) {
- hash = SvUVX(keysv);
+ if (HvREHASH(hv)) {
+ PERL_HASH_INTERNAL(hash, key, klen);
+ /* We don't have a pointer to the hv, so we have to replicate the
+ flag into every HEK, so that hv_iterkeysv can see it. */
+ /* And yes, you do need this even though you are not "storing" because
+ you can flip the flags below if doing an lval lookup. (And that
+ was put in to give the semantics Andreas was expecting.) */
+ flags |= HVhek_REHASH;
+ } else if (!hash) {
+ if (keysv && (SvIsCOW_shared_hash(keysv))) {
+ hash = SvSHARED_HASH(keysv);
} else {
PERL_HASH(hash, key, klen);
}
}
- /* entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
- entry = ((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
+ masked_flags = (flags & HVhek_MASK);
+
+#ifdef DYNAMIC_ENV_FETCH
+ if (!HvARRAY(hv)) entry = Null(HE*);
+ else
+#endif
+ {
+ entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)];
+ }
for (; entry; entry = HeNEXT(entry)) {
if (HeHASH(entry) != hash) /* strings can't be equal */
continue;
continue;
if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen)) /* is this it? */
continue;
- if ((HeKFLAGS(entry) ^ flags) & HVhek_UTF8)
+ if ((HeKFLAGS(entry) ^ masked_flags) & HVhek_UTF8)
continue;
- if (lval && HeKFLAGS(entry) != flags) {
- /* We match if HVhek_UTF8 bit in our flags and hash key's match.
- But if entry was set previously with HVhek_WASUTF8 and key now
- doesn't (or vice versa) then we should change the key's flag,
- as this is assignment. */
- if (HvSHAREKEYS(hv)) {
- /* 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. */
- int flags_nofree = flags & ~HVhek_FREEKEY;
- HEK *new_hek = share_hek_flags(key, klen, hash, flags_nofree);
- unshare_hek (HeKEY_hek(entry));
- HeKEY_hek(entry) = new_hek;
- }
- else
- HeKFLAGS(entry) = flags;
- }
- if (key != keysave)
- Safefree(key);
- /* if we find a placeholder, we pretend we haven't found anything */
- if (HeVAL(entry) == &PL_sv_undef)
+
+ if (action & (HV_FETCH_LVALUE|HV_FETCH_ISSTORE)) {
+ if (HeKFLAGS(entry) != masked_flags) {
+ /* We match if HVhek_UTF8 bit in our flags and hash key's
+ match. But if entry was set previously with HVhek_WASUTF8
+ and key now doesn't (or vice versa) then we should change
+ the key's flag, as this is assignment. */
+ if (HvSHAREKEYS(hv)) {
+ /* 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 = 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)
+ HvHASKFLAGS_on(hv);
+ }
+ if (HeVAL(entry) == &PL_sv_placeholder) {
+ /* yes, can store into placeholder slot */
+ if (action & HV_FETCH_LVALUE) {
+ if (SvMAGICAL(hv)) {
+ /* This preserves behaviour with the old hv_fetch
+ implementation which at this point would bail out
+ with a break; (at "if we find a placeholder, we
+ pretend we haven't found anything")
+
+ That break mean that if a placeholder were found, it
+ caused a call into hv_store, which in turn would
+ check magic, and if there is no magic end up pretty
+ much back at this point (in hv_store's code). */
+ break;
+ }
+ /* LVAL fetch which actaully needs a store. */
+ val = NEWSV(61,0);
+ HvPLACEHOLDERS(hv)--;
+ } else {
+ /* store */
+ 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) {
+ /* if we find a placeholder, we pretend we haven't found
+ anything */
break;
+ }
+ if (flags & HVhek_FREEKEY)
+ Safefree(key);
return entry;
}
#ifdef DYNAMIC_ENV_FETCH /* %ENV lookup? If so, try to fetch the value now */
- if (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) {
+ 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);
- return hv_store_ent(hv,keysv,sv,hash);
+ return hv_fetch_common(hv,keysv,key,klen,flags,HV_FETCH_ISSTORE,sv,
+ hash);
}
}
#endif
- if (!entry && SvREADONLY(hv)) {
+
+ 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 (flags & HVhek_FREEKEY)
- Safefree(key);
- if (lval) { /* gonna assign to this, so it better be there */
- sv = NEWSV(61,0);
- return hv_store_ent(hv,keysv,sv,hash);
+ if (!(action & (HV_FETCH_LVALUE|HV_FETCH_ISSTORE))) {
+ /* Not doing some form of store, so return failure. */
+ if (flags & HVhek_FREEKEY)
+ Safefree(key);
+ return 0;
+ }
+ if (action & HV_FETCH_LVALUE) {
+ val = NEWSV(61,0);
+ if (SvMAGICAL(hv)) {
+ /* At this point the old hv_fetch code would call to hv_store,
+ which in turn might do some tied magic. So we need to make that
+ magic check happen. */
+ /* gonna assign to this, so it better be there */
+ return hv_fetch_common(hv, keysv, key, klen, flags,
+ HV_FETCH_ISSTORE, val, hash);
+ /* XXX Surely that could leak if the fetch-was-store fails?
+ Just like the hv_fetch. */
+ }
+ }
+
+ /* Welcome to hv_store... */
+
+ 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. */
+ char *array;
+ Newz(503, array,
+ PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
+ char);
+ HvARRAY(hv) = (HE**)array;
+ }
+
+ oentry = &(HvARRAY(hv))[hash & (I32) xhv->xhv_max];
+
+ entry = new_HE();
+ /* share_hek_flags will do the free for us. This might be considered
+ bad API design. */
+ if (HvSHAREKEYS(hv))
+ 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;
+ HeNEXT(entry) = *oentry;
+ *oentry = entry;
+
+ if (val == &PL_sv_placeholder)
+ HvPLACEHOLDERS(hv)++;
+ if (masked_flags & HVhek_ENABLEHVKFLAGS)
+ HvHASKFLAGS_on(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 0;
+
+ return entry;
}
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) {
case PERL_MAGIC_tied:
case PERL_MAGIC_sig:
*needs_store = FALSE;
+ return; /* We've set all there is to set. */
}
}
mg = mg->mg_moremagic;
}
/*
-=for apidoc hv_store
-
-Stores an SV in a hash. The hash key is specified as C<key> and C<klen> is
-the length of the key. The C<hash> parameter is the precomputed hash
-value; if it is zero then Perl will compute it. The return value will be
-NULL if the operation failed or if the value did not need to be actually
-stored within the hash (as in the case of tied hashes). Otherwise it can
-be dereferenced to get the original C<SV*>. Note that the caller is
-responsible for suitably incrementing the reference count of C<val> before
-the call, and decrementing it if the function returned NULL. Effectively
-a successful hv_store takes ownership of one reference to C<val>. This is
-usually what you want; a newly created SV has a reference count of one, so
-if all your code does is create SVs then store them in a hash, hv_store
-will own the only reference to the new SV, and your code doesn't need to do
-anything further to tidy up. hv_store is not implemented as a call to
-hv_store_ent, and does not create a temporary SV for the key, so if your
-key data is not already in SV form then use hv_store in preference to
-hv_store_ent.
+=for apidoc hv_scalar
-See L<perlguts/"Understanding the Magic of Tied Hashes and Arrays"> for more
-information on how to use this function on tied hashes.
+Evaluates the hash in scalar context and returns the result. Handles magic when the hash is tied.
=cut
*/
-SV**
-Perl_hv_store(pTHX_ HV *hv, const char *key, I32 klen, SV *val, U32 hash)
+SV *
+Perl_hv_scalar(pTHX_ HV *hv)
{
- bool is_utf8 = FALSE;
- const char *keysave = key;
- int flags = 0;
-
- if (klen < 0) {
- klen = -klen;
- is_utf8 = TRUE;
- }
-
- if (is_utf8) {
- STRLEN tmplen = klen;
- /* Just casting the &klen to (STRLEN) won't work well
- * if STRLEN and I32 are of different widths. --jhi */
- key = (char*)bytes_from_utf8((U8*)key, &tmplen, &is_utf8);
- klen = tmplen;
- /* If we were able to downgrade here, then than means that we were
- passed in a key which only had chars 0-255, but was utf8 encoded. */
- if (is_utf8)
- flags = HVhek_UTF8;
- /* If we found we were able to downgrade the string to bytes, then
- we should flag that it needs upgrading on keys or each. */
- if (key != keysave)
- flags |= HVhek_WASUTF8 | HVhek_FREEKEY;
- }
-
- return hv_store_flags (hv, key, klen, val, hash, flags);
+ MAGIC *mg;
+ SV *sv;
+
+ if ((SvRMAGICAL(hv) && (mg = mg_find((SV*)hv, PERL_MAGIC_tied)))) {
+ sv = magic_scalarpack(hv, mg);
+ return sv;
+ }
+
+ sv = sv_newmortal();
+ if (HvFILL((HV*)hv))
+ Perl_sv_setpvf(aTHX_ sv, "%ld/%ld",
+ (long)HvFILL(hv), (long)HvMAX(hv) + 1);
+ else
+ sv_setiv(sv, 0);
+
+ return sv;
}
-SV**
-Perl_hv_store_flags(pTHX_ HV *hv, const char *key, I32 klen, SV *val,
- register U32 hash, int flags)
-{
- register XPVHV* xhv;
- register I32 i;
- register HE *entry;
- register HE **oentry;
-
- if (!hv)
- return 0;
-
- xhv = (XPVHV*)SvANY(hv);
- if (SvMAGICAL(hv)) {
- bool needs_copy;
- bool needs_store;
- hv_magic_check (hv, &needs_copy, &needs_store);
- if (needs_copy) {
- mg_copy((SV*)hv, val, key, klen);
- if (!xhv->xhv_array /* !HvARRAY */ && !needs_store) {
- if (flags & HVhek_FREEKEY)
- Safefree(key);
- return 0;
- }
-#ifdef ENV_IS_CASELESS
- else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
- key = savepvn(key,klen);
- key = (const char*)strupr((char*)key);
- hash = 0;
- }
-#endif
- }
- }
-
- if (flags)
- HvHASKFLAGS_on((SV*)hv);
-
- if (!hash)
- PERL_HASH(hash, key, klen);
-
- if (!xhv->xhv_array /* !HvARRAY(hv) */)
- Newz(505, xhv->xhv_array /* HvARRAY(hv) */,
- PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
- char);
-
- /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
- oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
- i = 1;
+/*
+=for apidoc hv_delete
- for (entry = *oentry; entry; i=0, entry = HeNEXT(entry)) {
- if (HeHASH(entry) != hash) /* strings can't be equal */
- continue;
- if (HeKLEN(entry) != (I32)klen)
- continue;
- if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen)) /* is this it? */
- continue;
- if ((HeKFLAGS(entry) ^ flags) & HVhek_UTF8)
- continue;
- if (HeVAL(entry) == &PL_sv_undef)
- xhv->xhv_placeholders--; /* yes, can store into placeholder slot */
- else
- SvREFCNT_dec(HeVAL(entry));
- if (flags & HVhek_PLACEHOLD) {
- /* We have been requested to insert a placeholder. Currently
- only Storable is allowed to do this. */
- xhv->xhv_placeholders++;
- HeVAL(entry) = &PL_sv_undef;
- } else
- HeVAL(entry) = val;
-
- if (HeKFLAGS(entry) != flags) {
- /* We match if HVhek_UTF8 bit in our flags and hash key's match.
- But if entry was set previously with HVhek_WASUTF8 and key now
- doesn't (or vice versa) then we should change the key's flag,
- as this is assignment. */
- if (HvSHAREKEYS(hv)) {
- /* 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. */
- int flags_nofree = flags & ~HVhek_FREEKEY;
- HEK *new_hek = share_hek_flags(key, klen, hash, flags_nofree);
- unshare_hek (HeKEY_hek(entry));
- HeKEY_hek(entry) = new_hek;
- }
- else
- HeKFLAGS(entry) = flags;
- }
- if (flags & HVhek_FREEKEY)
- Safefree(key);
- return &HeVAL(entry);
- }
+Deletes a key/value pair in the hash. The value SV is removed from the
+hash and returned to the caller. The C<klen> is the length of the key.
+The C<flags> value will normally be zero; if set to G_DISCARD then NULL
+will be returned.
- if (SvREADONLY(hv)) {
- S_hv_notallowed(aTHX_ flags, key, klen,
- "access disallowed key '%"SVf"' to"
- );
- }
+=cut
+*/
- entry = new_HE();
- /* share_hek_flags will do the free for us. This might be considered
- bad API design. */
- if (HvSHAREKEYS(hv))
- HeKEY_hek(entry) = share_hek_flags(key, klen, hash, flags);
- else /* gotta do the real thing */
- HeKEY_hek(entry) = save_hek_flags(key, klen, hash, flags);
- if (flags & HVhek_PLACEHOLD) {
- /* We have been requested to insert a placeholder. Currently
- only Storable is allowed to do this. */
- xhv->xhv_placeholders++;
- HeVAL(entry) = &PL_sv_undef;
- } else
- HeVAL(entry) = val;
- HeNEXT(entry) = *oentry;
- *oentry = entry;
+SV *
+Perl_hv_delete(pTHX_ HV *hv, const char *key, I32 klen_i32, I32 flags)
+{
+ STRLEN klen;
+ int k_flags = 0;
- xhv->xhv_keys++; /* HvKEYS(hv)++ */
- if (i) { /* initial entry? */
- xhv->xhv_fill++; /* HvFILL(hv)++ */
- } else if (xhv->xhv_keys > (IV)xhv->xhv_max /* HvKEYS(hv) > HvMAX(hv) */) {
- hsplit(hv);
+ if (klen_i32 < 0) {
+ klen = -klen_i32;
+ k_flags |= HVhek_UTF8;
+ } else {
+ klen = klen_i32;
}
-
- return &HeVAL(entry);
+ return hv_delete_common(hv, NULL, key, klen, k_flags, flags, 0);
}
/*
-=for apidoc hv_store_ent
-
-Stores C<val> in a hash. The hash key is specified as C<key>. The C<hash>
-parameter is the precomputed hash value; if it is zero then Perl will
-compute it. The return value is the new hash entry so created. It will be
-NULL if the operation failed or if the value did not need to be actually
-stored within the hash (as in the case of tied hashes). Otherwise the
-contents of the return value can be accessed using the C<He?> macros
-described here. Note that the caller is responsible for suitably
-incrementing the reference count of C<val> before the call, and
-decrementing it if the function returned NULL. Effectively a successful
-hv_store_ent takes ownership of one reference to C<val>. This is
-usually what you want; a newly created SV has a reference count of one, so
-if all your code does is create SVs then store them in a hash, hv_store
-will own the only reference to the new SV, and your code doesn't need to do
-anything further to tidy up. Note that hv_store_ent only reads the C<key>;
-unlike C<val> it does not take ownership of it, so maintaining the correct
-reference count on C<key> is entirely the caller's responsibility. hv_store
-is not implemented as a call to hv_store_ent, and does not create a temporary
-SV for the key, so if your key data is not already in SV form then use
-hv_store in preference to hv_store_ent.
+=for apidoc hv_delete_ent
-See L<perlguts/"Understanding the Magic of Tied Hashes and Arrays"> for more
-information on how to use this function on tied hashes.
+Deletes a key/value pair in the hash. The value SV is removed from the
+hash and returned to the caller. The C<flags> value will normally be zero;
+if set to G_DISCARD then NULL will be returned. C<hash> can be a valid
+precomputed hash value, or 0 to ask for it to be computed.
=cut
*/
-HE *
-Perl_hv_store_ent(pTHX_ HV *hv, SV *keysv, SV *val, U32 hash)
+SV *
+Perl_hv_delete_ent(pTHX_ HV *hv, SV *keysv, I32 flags, U32 hash)
{
- XPVHV* xhv;
- char *key;
- STRLEN klen;
- I32 i;
- HE *entry;
- HE **oentry;
+ return hv_delete_common(hv, keysv, NULL, 0, 0, flags, hash);
+}
+
+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 HE *entry;
+ register HE **oentry;
+ HE *const *first_entry;
+ SV *sv;
bool is_utf8;
- int flags = 0;
- char *keysave;
+ int masked_flags;
if (!hv)
- return 0;
+ return Nullsv;
- xhv = (XPVHV*)SvANY(hv);
- if (SvMAGICAL(hv)) {
+ if (keysv) {
+ if (k_flags & HVhek_FREEKEY)
+ Safefree(key);
+ key = SvPV_const(keysv, klen);
+ k_flags = 0;
+ is_utf8 = (SvUTF8(keysv) != 0);
+ } else {
+ is_utf8 = ((k_flags & HVhek_UTF8) ? TRUE : FALSE);
+ }
+
+ if (SvRMAGICAL(hv)) {
bool needs_copy;
bool needs_store;
hv_magic_check (hv, &needs_copy, &needs_store);
+
if (needs_copy) {
- bool save_taint = PL_tainted;
- if (PL_tainting)
- PL_tainted = SvTAINTED(keysv);
- keysv = sv_2mortal(newSVsv(keysv));
- mg_copy((SV*)hv, val, (char*)keysv, HEf_SVKEY);
- TAINT_IF(save_taint);
- if (!xhv->xhv_array /* !HvARRAY(hv) */ && !needs_store)
- return Nullhe;
+ entry = hv_fetch_common(hv, keysv, key, klen,
+ k_flags & ~HVhek_FREEKEY, HV_FETCH_LVALUE,
+ Nullsv, hash);
+ sv = entry ? HeVAL(entry) : NULL;
+ if (sv) {
+ if (SvMAGICAL(sv)) {
+ mg_clear(sv);
+ }
+ if (!needs_store) {
+ if (mg_find(sv, PERL_MAGIC_tiedelem)) {
+ /* No longer an element */
+ sv_unmagic(sv, PERL_MAGIC_tiedelem);
+ return sv;
+ }
+ return Nullsv; /* element cannot be deleted */
+ }
#ifdef ENV_IS_CASELESS
- else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
- key = SvPV(keysv, klen);
- keysv = sv_2mortal(newSVpvn(key,klen));
- (void)strupr(SvPVX(keysv));
- hash = 0;
- }
-#endif
- }
- }
-
- keysave = key = SvPV(keysv, klen);
- is_utf8 = (SvUTF8(keysv) != 0);
-
- if (is_utf8) {
- key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8);
- if (is_utf8)
- flags = HVhek_UTF8;
- if (key != keysave)
- flags |= HVhek_WASUTF8 | HVhek_FREEKEY;
- HvHASKFLAGS_on((SV*)hv);
- }
-
- if (!hash) {
- if SvIsCOW_shared_hash(keysv) {
- hash = SvUVX(keysv);
- } else {
- PERL_HASH(hash, key, klen);
- }
- }
-
- if (!xhv->xhv_array /* !HvARRAY(hv) */)
- Newz(505, xhv->xhv_array /* HvARRAY(hv) */,
- PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
- char);
-
- /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
- oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
- i = 1;
- entry = *oentry;
- for (; entry; i=0, entry = HeNEXT(entry)) {
- if (HeHASH(entry) != hash) /* strings can't be equal */
- continue;
- if (HeKLEN(entry) != (I32)klen)
- continue;
- if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen)) /* is this it? */
- continue;
- if ((HeKFLAGS(entry) ^ flags) & HVhek_UTF8)
- continue;
- if (HeVAL(entry) == &PL_sv_undef)
- xhv->xhv_placeholders--; /* yes, can store into placeholder slot */
- else
- SvREFCNT_dec(HeVAL(entry));
- HeVAL(entry) = val;
- if (HeKFLAGS(entry) != flags) {
- /* We match if HVhek_UTF8 bit in our flags and hash key's match.
- But if entry was set previously with HVhek_WASUTF8 and key now
- doesn't (or vice versa) then we should change the key's flag,
- as this is assignment. */
- if (HvSHAREKEYS(hv)) {
- /* 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. */
- int flags_nofree = flags & ~HVhek_FREEKEY;
- HEK *new_hek = share_hek_flags(key, klen, hash, flags_nofree);
- unshare_hek (HeKEY_hek(entry));
- HeKEY_hek(entry) = new_hek;
- }
- else
- HeKFLAGS(entry) = flags;
- }
- if (flags & HVhek_FREEKEY)
- Safefree(key);
- return entry;
- }
-
- if (SvREADONLY(hv)) {
- S_hv_notallowed(aTHX_ flags, key, klen,
- "access disallowed key '%"SVf"' to"
- );
- }
-
- entry = new_HE();
- /* share_hek_flags will do the free for us. This might be considered
- bad API design. */
- if (HvSHAREKEYS(hv))
- HeKEY_hek(entry) = share_hek_flags(key, klen, hash, flags);
- else /* gotta do the real thing */
- HeKEY_hek(entry) = save_hek_flags(key, klen, hash, flags);
- HeVAL(entry) = val;
- HeNEXT(entry) = *oentry;
- *oentry = entry;
-
- xhv->xhv_keys++; /* HvKEYS(hv)++ */
- if (i) { /* initial entry? */
- xhv->xhv_fill++; /* HvFILL(hv)++ */
- if (xhv->xhv_keys > (IV)xhv->xhv_max /* HvKEYS(hv) > HvMAX(hv) */)
- hsplit(hv);
- }
-
- return entry;
-}
-
-/*
-=for apidoc hv_delete
-
-Deletes a key/value pair in the hash. The value SV is removed from the
-hash and returned to the caller. The C<klen> is the length of the key.
-The C<flags> value will normally be zero; if set to G_DISCARD then NULL
-will be returned.
-
-=cut
-*/
-
-SV *
-Perl_hv_delete(pTHX_ HV *hv, const char *key, I32 klen, I32 flags)
-{
- register XPVHV* xhv;
- register I32 i;
- register U32 hash;
- register HE *entry;
- register HE **oentry;
- SV **svp;
- SV *sv;
- bool is_utf8 = FALSE;
- int k_flags = 0;
- const char *keysave = key;
-
- if (!hv)
- return Nullsv;
- if (klen < 0) {
- klen = -klen;
- is_utf8 = TRUE;
- }
- if (SvRMAGICAL(hv)) {
- bool needs_copy;
- bool needs_store;
- hv_magic_check (hv, &needs_copy, &needs_store);
-
- if (needs_copy && (svp = hv_fetch(hv, key, klen, TRUE))) {
- sv = *svp;
- if (SvMAGICAL(sv)) {
- mg_clear(sv);
- }
- if (!needs_store) {
- if (mg_find(sv, PERL_MAGIC_tiedelem)) {
- /* No longer an element */
- sv_unmagic(sv, PERL_MAGIC_tiedelem);
- return sv;
+ 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;
}
- return Nullsv; /* element cannot be deleted */
- }
-#ifdef ENV_IS_CASELESS
- else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
- sv = sv_2mortal(newSVpvn(key,klen));
- key = strupr(SvPVX(sv));
- }
#endif
- }
- }
- xhv = (XPVHV*)SvANY(hv);
- if (!xhv->xhv_array /* !HvARRAY(hv) */)
- return Nullsv;
-
- if (is_utf8) {
- STRLEN tmplen = klen;
- /* See the note in hv_fetch(). --jhi */
- key = (char*)bytes_from_utf8((U8*)key, &tmplen, &is_utf8);
- klen = tmplen;
- if (is_utf8)
- k_flags = HVhek_UTF8;
- if (key != keysave)
- k_flags |= HVhek_FREEKEY;
- }
-
- PERL_HASH(hash, key, klen);
-
- /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
- oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
- entry = *oentry;
- i = 1;
- for (; entry; i=0, oentry = &HeNEXT(entry), entry = *oentry) {
- if (HeHASH(entry) != hash) /* strings can't be equal */
- continue;
- if (HeKLEN(entry) != (I32)klen)
- continue;
- if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen)) /* is this it? */
- continue;
- if ((HeKFLAGS(entry) ^ k_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_undef)
- {
- if (SvREADONLY(hv))
- return Nullsv; /* if still SvREADONLY, leave it deleted. */
- else {
- /* 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;
- }
- }
- else if (SvREADONLY(hv) && HeVAL(entry) && SvREADONLY(HeVAL(entry))) {
- S_hv_notallowed(aTHX_ k_flags, key, klen,
- "delete readonly key '%"SVf"' from"
- );
- }
-
- if (flags & G_DISCARD)
- sv = Nullsv;
- else {
- sv = sv_2mortal(HeVAL(entry));
- HeVAL(entry) = &PL_sv_undef;
- }
-
- /*
- * If a restricted hash, rather than really deleting the entry, put
- * a placeholder there. This marks the key as being "approved", so
- * we can still access via not-really-existing key without raising
- * an error.
- */
- if (SvREADONLY(hv)) {
- HeVAL(entry) = &PL_sv_undef;
- /* 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)++ */
- } else {
- *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);
- }
- return sv;
- }
- if (SvREADONLY(hv)) {
- S_hv_notallowed(aTHX_ k_flags, key, klen,
- "access disallowed key '%"SVf"' from"
- );
- }
-
- if (k_flags & HVhek_FREEKEY)
- Safefree(key);
- return Nullsv;
-}
-
-/*
-=for apidoc hv_delete_ent
-
-Deletes a key/value pair in the hash. The value SV is removed from the
-hash and returned to the caller. The C<flags> value will normally be zero;
-if set to G_DISCARD then NULL will be returned. C<hash> can be a valid
-precomputed hash value, or 0 to ask for it to be computed.
-
-=cut
-*/
-
-SV *
-Perl_hv_delete_ent(pTHX_ HV *hv, SV *keysv, I32 flags, U32 hash)
-{
- register XPVHV* xhv;
- register I32 i;
- register char *key;
- STRLEN klen;
- register HE *entry;
- register HE **oentry;
- SV *sv;
- bool is_utf8;
- int k_flags = 0;
- char *keysave;
-
- if (!hv)
- return Nullsv;
- if (SvRMAGICAL(hv)) {
- bool needs_copy;
- bool needs_store;
- hv_magic_check (hv, &needs_copy, &needs_store);
-
- if (needs_copy && (entry = hv_fetch_ent(hv, keysv, TRUE, hash))) {
- sv = HeVAL(entry);
- if (SvMAGICAL(sv)) {
- mg_clear(sv);
- }
- if (!needs_store) {
- if (mg_find(sv, PERL_MAGIC_tiedelem)) {
- /* No longer an element */
- sv_unmagic(sv, PERL_MAGIC_tiedelem);
- return sv;
- }
- return Nullsv; /* element cannot be deleted */
- }
-#ifdef ENV_IS_CASELESS
- else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
- key = SvPV(keysv, klen);
- keysv = sv_2mortal(newSVpvn(key,klen));
- (void)strupr(SvPVX(keysv));
- hash = 0;
}
-#endif
}
}
xhv = (XPVHV*)SvANY(hv);
- if (!xhv->xhv_array /* !HvARRAY(hv) */)
+ if (!HvARRAY(hv))
return Nullsv;
- keysave = key = SvPV(keysv, klen);
- is_utf8 = (SvUTF8(keysv) != 0);
-
if (is_utf8) {
+ const char *keysave = key;
key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8);
- if (is_utf8)
- k_flags = HVhek_UTF8;
- if (key != keysave)
- k_flags |= HVhek_FREEKEY;
- }
-
- if (!hash)
- PERL_HASH(hash, key, klen);
-
- /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
- oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
- entry = *oentry;
- i = 1;
- for (; entry; i=0, oentry = &HeNEXT(entry), entry = *oentry) {
- if (HeHASH(entry) != hash) /* strings can't be equal */
- continue;
- if (HeKLEN(entry) != (I32)klen)
- continue;
- if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen)) /* is this it? */
- continue;
- if ((HeKFLAGS(entry) ^ k_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_undef)
- {
- 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;
- }
- else if (SvREADONLY(hv) && HeVAL(entry) && SvREADONLY(HeVAL(entry))) {
- S_hv_notallowed(aTHX_ k_flags, key, klen,
- "delete readonly key '%"SVf"' from"
- );
- }
-
- if (flags & G_DISCARD)
- sv = Nullsv;
- else {
- sv = sv_2mortal(HeVAL(entry));
- HeVAL(entry) = &PL_sv_undef;
- }
-
- /*
- * If a restricted hash, rather than really deleting the entry, put
- * a placeholder there. This marks the key as being "approved", so
- * we can still access via not-really-existing key without raising
- * an error.
- */
- if (SvREADONLY(hv)) {
- HeVAL(entry) = &PL_sv_undef;
- /* 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)++ */
- } else {
- *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);
- }
- return sv;
- }
- if (SvREADONLY(hv)) {
- S_hv_notallowed(aTHX_ k_flags, key, klen,
- "delete disallowed key '%"SVf"' from"
- );
- }
-
- if (k_flags & HVhek_FREEKEY)
- Safefree(key);
- return Nullsv;
-}
-
-/*
-=for apidoc hv_exists
-
-Returns a boolean indicating whether the specified hash key exists. The
-C<klen> is the length of the key.
-
-=cut
-*/
-
-bool
-Perl_hv_exists(pTHX_ HV *hv, const char *key, I32 klen)
-{
- register XPVHV* xhv;
- register U32 hash;
- register HE *entry;
- SV *sv;
- bool is_utf8 = FALSE;
- const char *keysave = key;
- int k_flags = 0;
-
- if (!hv)
- return 0;
-
- if (klen < 0) {
- klen = -klen;
- is_utf8 = TRUE;
- }
-
- if (SvRMAGICAL(hv)) {
- if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) {
- sv = sv_newmortal();
- mg_copy((SV*)hv, sv, key, klen);
- magic_existspack(sv, mg_find(sv, PERL_MAGIC_tiedelem));
- return (bool)SvTRUE(sv);
- }
-#ifdef ENV_IS_CASELESS
- else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
- sv = sv_2mortal(newSVpvn(key,klen));
- key = strupr(SvPVX(sv));
- }
-#endif
- }
-
- xhv = (XPVHV*)SvANY(hv);
-#ifndef DYNAMIC_ENV_FETCH
- if (!xhv->xhv_array /* !HvARRAY(hv) */)
- return 0;
-#endif
-
- if (is_utf8) {
- STRLEN tmplen = klen;
- /* See the note in hv_fetch(). --jhi */
- key = (char*)bytes_from_utf8((U8*)key, &tmplen, &is_utf8);
- klen = tmplen;
if (is_utf8)
- k_flags = HVhek_UTF8;
- if (key != keysave)
- k_flags |= HVhek_FREEKEY;
- }
-
- PERL_HASH(hash, key, klen);
-
-#ifdef DYNAMIC_ENV_FETCH
- if (!xhv->xhv_array /* !HvARRAY(hv) */) entry = Null(HE*);
- else
-#endif
- /* entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
- entry = ((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
- for (; entry; entry = HeNEXT(entry)) {
- if (HeHASH(entry) != hash) /* strings can't be equal */
- continue;
- if (HeKLEN(entry) != klen)
- continue;
- if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen)) /* is this it? */
- continue;
- if ((HeKFLAGS(entry) ^ k_flags) & HVhek_UTF8)
- continue;
- if (k_flags & HVhek_FREEKEY)
- Safefree(key);
- /* If we find the key, but the value is a placeholder, return false. */
- if (HeVAL(entry) == &PL_sv_undef)
- return FALSE;
-
- return TRUE;
- }
-#ifdef DYNAMIC_ENV_FETCH /* is it out there? */
- if (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) {
- unsigned long len;
- char *env = PerlEnv_ENVgetenv_len(key,&len);
- if (env) {
- sv = newSVpvn(env,len);
- SvTAINTED_on(sv);
- (void)hv_store(hv,key,klen,sv,hash);
- if (k_flags & HVhek_FREEKEY)
- Safefree(key);
- return TRUE;
- }
- }
-#endif
- if (k_flags & HVhek_FREEKEY)
- Safefree(key);
- return FALSE;
-}
-
-
-/*
-=for apidoc hv_exists_ent
-
-Returns a boolean indicating whether the specified hash key exists. C<hash>
-can be a valid precomputed hash value, or 0 to ask for it to be
-computed.
-
-=cut
-*/
-
-bool
-Perl_hv_exists_ent(pTHX_ HV *hv, SV *keysv, U32 hash)
-{
- register XPVHV* xhv;
- register char *key;
- STRLEN klen;
- register HE *entry;
- SV *sv;
- bool is_utf8;
- char *keysave;
- int k_flags = 0;
-
- if (!hv)
- return 0;
-
- if (SvRMAGICAL(hv)) {
- if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) {
- SV* svret = sv_newmortal();
- sv = sv_newmortal();
- keysv = sv_2mortal(newSVsv(keysv));
- mg_copy((SV*)hv, sv, (char*)keysv, HEf_SVKEY);
- magic_existspack(svret, mg_find(sv, PERL_MAGIC_tiedelem));
- return (bool)SvTRUE(svret);
- }
-#ifdef ENV_IS_CASELESS
- else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
- key = SvPV(keysv, klen);
- keysv = sv_2mortal(newSVpvn(key,klen));
- (void)strupr(SvPVX(keysv));
- hash = 0;
+ k_flags |= HVhek_UTF8;
+ else
+ k_flags &= ~HVhek_UTF8;
+ if (key != keysave) {
+ if (k_flags & HVhek_FREEKEY) {
+ /* This shouldn't happen if our caller does what we expect,
+ but strictly the API allows it. */
+ Safefree(keysave);
+ }
+ k_flags |= HVhek_WASUTF8 | HVhek_FREEKEY;
}
-#endif
- }
-
- xhv = (XPVHV*)SvANY(hv);
-#ifndef DYNAMIC_ENV_FETCH
- if (!xhv->xhv_array /* !HvARRAY(hv) */)
- return 0;
-#endif
+ HvHASKFLAGS_on((SV*)hv);
+ }
- keysave = key = SvPV(keysv, klen);
- is_utf8 = (SvUTF8(keysv) != 0);
- if (is_utf8) {
- key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8);
- if (is_utf8)
- k_flags = HVhek_UTF8;
- if (key != keysave)
- k_flags |= HVhek_FREEKEY;
+ if (HvREHASH(hv)) {
+ PERL_HASH_INTERNAL(hash, key, klen);
+ } else if (!hash) {
+ if (keysv && (SvIsCOW_shared_hash(keysv))) {
+ hash = SvSHARED_HASH(keysv);
+ } else {
+ PERL_HASH(hash, key, klen);
+ }
}
- if (!hash)
- PERL_HASH(hash, key, klen);
-#ifdef DYNAMIC_ENV_FETCH
- if (!xhv->xhv_array /* !HvARRAY(hv) */) entry = Null(HE*);
- else
-#endif
- /* entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
- entry = ((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
- for (; entry; entry = HeNEXT(entry)) {
+ masked_flags = (k_flags & HVhek_MASK);
+
+ first_entry = oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)];
+ entry = *oentry;
+ for (; entry; oentry = &HeNEXT(entry), entry = *oentry) {
if (HeHASH(entry) != hash) /* strings can't be equal */
continue;
if (HeKLEN(entry) != (I32)klen)
continue;
if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen)) /* is this it? */
continue;
- if ((HeKFLAGS(entry) ^ k_flags) & HVhek_UTF8)
+ if ((HeKFLAGS(entry) ^ masked_flags) & HVhek_UTF8)
continue;
- if (k_flags & HVhek_FREEKEY)
- Safefree(key);
- /* If we find the key, but the value is a placeholder, return false. */
- if (HeVAL(entry) == &PL_sv_undef)
- return FALSE;
- return TRUE;
- }
-#ifdef DYNAMIC_ENV_FETCH /* is it out there? */
- if (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) {
- unsigned long len;
- char *env = PerlEnv_ENVgetenv_len(key,&len);
- if (env) {
- sv = newSVpvn(env,len);
- SvTAINTED_on(sv);
- (void)hv_store_ent(hv,keysv,sv,hash);
- if (k_flags & HVhek_FREEKEY)
- Safefree(key);
- return TRUE;
+
+ 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)
+ {
+ 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,
+ "Attempt to delete readonly key '%"SVf"' from"
+ " a restricted hash");
+ }
+ if (k_flags & HVhek_FREEKEY)
+ Safefree(key);
+
+ if (d_flags & G_DISCARD)
+ sv = Nullsv;
+ else {
+ sv = sv_2mortal(HeVAL(entry));
+ HeVAL(entry) = &PL_sv_placeholder;
+ }
+
+ /*
+ * If a restricted hash, rather than really deleting the entry, put
+ * a placeholder there. This marks the key as being "approved", so
+ * we can still access via not-really-existing key without raising
+ * 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 */
+ HvPLACEHOLDERS(hv)++;
+ } else {
+ *oentry = HeNEXT(entry);
+ if(!*first_entry) {
+ xhv->xhv_fill--; /* HvFILL(hv)-- */
+ }
+ if (SvOOK(hv) && entry == HvAUX(hv)->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);
}
+ return sv;
}
-#endif
+ if (SvREADONLY(hv)) {
+ S_hv_notallowed(aTHX_ k_flags, key, klen,
+ "Attempt to delete disallowed key '%"SVf"' from"
+ " a restricted hash");
+ }
+
if (k_flags & HVhek_FREEKEY)
- Safefree(key);
- return FALSE;
+ Safefree(key);
+ return Nullsv;
}
STATIC void
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_get(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);
+ 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);
+ New(2, a, PERL_HV_ARRAY_ALLOC_BYTES(newsize)
+ + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char);
if (!a) {
PL_nomemok = FALSE;
return;
}
- Copy(xhv->xhv_array /* HvARRAY(hv) */, a, oldsize * sizeof(HE*), char);
+ 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(xhv->xhv_array /* HvARRAY(hv) */,
- PERL_HV_ARRAY_ALLOC_BYTES(oldsize));
+ offer_nice_chunk(HvARRAY(hv),
+ PERL_HV_ARRAY_ALLOC_BYTES(oldsize)
+ + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0));
}
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;
bep = aep+oldsize;
if (!*bep)
xhv->xhv_fill++; /* HvFILL(hv)++ */
*bep = entry;
+ right_length++;
continue;
}
- else
+ else {
oentry = &HeNEXT(entry);
+ left_length++;
+ }
}
if (!*aep) /* everything moved */
xhv->xhv_fill--; /* HvFILL(hv)-- */
+ /* I think we don't actually need to keep track of the longest length,
+ merely flag if anything is too long. But for the moment while
+ developing this code I'll track it. */
+ if (left_length > longest_chain)
+ longest_chain = left_length;
+ if (right_length > longest_chain)
+ longest_chain = right_length;
+ }
+
+
+ /* Pick your policy for "hashing isn't working" here: */
+ if (longest_chain <= HV_MAX_LENGTH_BEFORE_SPLIT /* split worked? */
+ || HvREHASH(hv)) {
+ return;
+ }
+
+ if (hv == PL_strtab) {
+ /* Urg. Someone is doing something nasty to the string table.
+ Can't win. */
+ return;
+ }
+
+ /* Awooga. Awooga. Pathological data. */
+ /*PerlIO_printf(PerlIO_stderr(), "%p %d of %d with %d/%d buckets\n", hv,
+ longest_chain, HvTOTALKEYS(hv), HvFILL(hv), 1+HvMAX(hv));*/
+
+ ++newsize;
+ Newz(2, 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;
+ HvSHAREKEYS_off(hv);
+ HvREHASH_on(hv);
+
+ aep = HvARRAY(hv);
+
+ for (i=0; i<newsize; i++,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 * const next = HeNEXT(entry);
+ UV hash;
+ HE **bep;
+
+ /* Rehash it */
+ PERL_HASH_INTERNAL(hash, HeKEY(entry), HeKLEN(entry));
+
+ if (was_shared) {
+ /* Unshare it. */
+ HEK *new_hek
+ = save_hek_flags(HeKEY(entry), HeKLEN(entry),
+ hash, HeKFLAGS(entry));
+ unshare_hek (HeKEY_hek(entry));
+ HeKEY_hek(entry) = new_hek;
+ } else {
+ /* Not shared, so simply write the new hash in. */
+ HeHASH(entry) = hash;
+ }
+ /*PerlIO_printf(PerlIO_stderr(), "%d ", HeKFLAGS(entry));*/
+ HEK_REHASH_on(HeKEY_hek(entry));
+ /*PerlIO_printf(PerlIO_stderr(), "%d\n", HeKFLAGS(entry));*/
+
+ /* Copy oentry to the correct new chain. */
+ bep = ((HE**)a) + (hash & (I32) xhv->xhv_max);
+ if (!*bep)
+ xhv->xhv_fill++; /* HvFILL(hv)++ */
+ HeNEXT(entry) = *bep;
+ *bep = entry;
+
+ entry = next;
+ }
}
+ 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)
- 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);
+ New(2, a, PERL_HV_ARRAY_ALLOC_BYTES(newsize)
+ + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char);
if (!a) {
PL_nomemok = FALSE;
return;
}
- Copy(xhv->xhv_array /* HvARRAY(hv) */, a, oldsize * sizeof(HE*), char);
+ 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(xhv->xhv_array /* HvARRAY(hv) */,
- PERL_HV_ARRAY_ALLOC_BYTES(oldsize));
+ offer_nice_chunk(HvARRAY(hv),
+ PERL_HV_ARRAY_ALLOC_BYTES(oldsize)
+ + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0));
}
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);
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);
#ifndef NODEFAULT_SHAREKEYS
HvSHAREKEYS_on(hv); /* key-sharing on by default */
#endif
+
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 */
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)
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;
+ DEBUG_A(Perl_hv_assert(aTHX_ hv));
+
xhv = (XPVHV*)SvANY(hv);
- if(SvREADONLY(hv)) {
+ 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];
+ STRLEN i;
+ for (i = 0; i <= xhv->xhv_max; i++) {
+ HE *entry = (HvARRAY(hv))[i];
for (; entry; entry = HeNEXT(entry)) {
/* not already placeholder */
- if (HeVAL(entry) != &PL_sv_undef) {
+ if (HeVAL(entry) != &PL_sv_placeholder) {
if (HeVAL(entry) && SvREADONLY(HeVAL(entry))) {
SV* keysv = hv_iterkeysv(entry);
Perl_croak(aTHX_
- "Attempt to delete readonly key '%_' from a restricted hash",
- keysv);
+ "Attempt to delete readonly key '%"SVf"' from a restricted hash",
+ keysv);
}
SvREFCNT_dec(HeVAL(entry));
- HeVAL(entry) = &PL_sv_undef;
- xhv->xhv_placeholders++; /* HvPLACEHOLDERS(hv)++ */
+ HeVAL(entry) = &PL_sv_placeholder;
+ HvPLACEHOLDERS(hv)++;
}
}
}
- return;
+ goto reset;
}
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))
mg_clear((SV*)hv);
HvHASKFLAGS_off(hv);
+ HvREHASH_off(hv);
+ reset:
+ if (SvOOK(hv)) {
+ HvEITER_set(hv, NULL);
+ }
+}
+
+/*
+=for apidoc hv_clear_placeholders
+
+Clears any placeholders from a hash. If a restricted hash has any of its keys
+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 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.
+
+=cut
+*/
+
+void
+Perl_hv_clear_placeholders(pTHX_ HV *hv)
+{
+ dVAR;
+ I32 items = (I32)HvPLACEHOLDERS_get(hv);
+ I32 i;
+
+ if (items == 0)
+ return;
+
+ i = HvMAX(hv);
+ 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_get(hv);
+ if (HvKEYS(hv) == 0)
+ HvHASKFLAGS_off(hv);
+ HvPLACEHOLDERS_set(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;
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) {
- oentry = entry;
+ register HE *oentry = entry;
entry = HeNEXT(entry);
hv_free_ent(hv, oentry);
}
entry = array[riter];
}
}
+
+ 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);
+ }
+ iter->xhv_riter = -1; /* HvRITER(hv) = -1 */
+ iter->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */
+ SvFLAGS(hv) |= SVf_OOK;
+ }
+
HvARRAY(hv) = array;
- (void)hv_iterinit(hv);
}
/*
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)) {
+ if ((name = HvNAME_get(hv))) {
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, HvNAMELEN_get(hv), G_DISCARD);
+ Perl_hv_name_set(aTHX_ hv, 0, 0, 0);
}
+ SvFLAGS(hv) &= ~SVf_OOK;
+ Safefree(HvARRAY(hv));
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);
}
+static struct xpvhv_aux*
+S_hv_auxinit(pTHX_ HV *hv) {
+ struct xpvhv_aux *iter;
+ char *array;
+
+ if (!HvARRAY(hv)) {
+ Newz(0, 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*) */
+ iter->xhv_name = 0;
+
+ return iter;
+}
+
/*
=for apidoc hv_iterinit
I32
Perl_hv_iterinit(pTHX_ HV *hv)
{
- register XPVHV* xhv;
HE *entry;
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);
+
+ 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);
+ hv_free_ent(hv, entry);
+ }
+ iter->xhv_riter = -1; /* HvRITER(hv) = -1 */
+ iter->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */
+ } else {
+ S_hv_auxinit(aTHX_ hv);
}
- 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);
+ return HvTOTALKEYS(hv);
+}
+
+I32 *
+Perl_hv_riter_p(pTHX_ HV *hv) {
+ struct xpvhv_aux *iter;
+
+ if (!hv)
+ Perl_croak(aTHX_ "Bad hash");
+
+ iter = SvOOK(hv) ? HvAUX(hv) : S_hv_auxinit(aTHX_ hv);
+ return &(iter->xhv_riter);
+}
+
+HE **
+Perl_hv_eiter_p(pTHX_ HV *hv) {
+ struct xpvhv_aux *iter;
+
+ if (!hv)
+ Perl_croak(aTHX_ "Bad hash");
+
+ iter = SvOOK(hv) ? HvAUX(hv) : S_hv_auxinit(aTHX_ hv);
+ 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");
+
+ if (SvOOK(hv)) {
+ iter = HvAUX(hv);
+ } else {
+ if (riter == -1)
+ return;
+
+ iter = S_hv_auxinit(aTHX_ hv);
+ }
+ 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");
+
+ 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;
+
+ 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;
+ U32 hash;
+ (void)flags;
+
+ if (SvOOK(hv)) {
+ iter = HvAUX(hv);
+ if (iter->xhv_name) {
+ unshare_hek_or_pvn(iter->xhv_name, 0, 0, 0);
+ }
+ } else {
+ if (name == 0)
+ return;
+
+ iter = S_hv_auxinit(aTHX_ hv);
+ }
+ PERL_HASH(hash, name, len);
+ iter->xhv_name = name ? share_hek(name, len, hash) : 0;
}
+
/*
=for apidoc hv_iternext
The C<flags> value will normally be zero; if HV_ITERNEXT_WANTPLACEHOLDERS is
set the placeholders keys (for restricted hashes) will be returned in addition
to normal keys. By default placeholders are automatically skipped over.
-Currently a placeholder is implemented with a value that is literally
-<&Perl_sv_undef> (a regular C<undef> value is a normal read-write SV for which
-C<!SvOK> is false). Note that the implementation of placeholders and
+Currently a placeholder is implemented with a value that is
+C<&Perl_sv_placeholder>. Note that the implementation of placeholders and
restricted hashes may change, and the implementation currently is
insufficiently abstracted for any change to be tidy.
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) */
+
+ 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 = HvAUX(hv);
+
+ 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) */,
- PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
- char);
+ /* hv_iterint now ensures this. */
+ assert (HvARRAY(hv));
+
/* At start of hash, entry is NULL. */
if (entry)
{
* Skip past any placeholders -- don't want to include them in
* any iteration.
*/
- while (entry && HeVAL(entry) == &PL_sv_undef) {
+ while (entry && HeVAL(entry) == &PL_sv_placeholder) {
entry = HeNEXT(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.
Try the next. */
- while (entry && HeVAL(entry) == &PL_sv_undef)
+ while (entry && HeVAL(entry) == &PL_sv_placeholder)
entry = HeNEXT(entry);
}
/* Will loop again if this linked list starts NULL
hv_free_ent(hv, oldentry);
}
- xhv->xhv_eiter = entry; /* HvEITER(hv) = entry */
+ /*if (HvREHASH(hv) && entry && !HeKREHASH(entry))
+ PerlIO_printf(PerlIO_stderr(), "Awooga %p %p\n", hv, entry);*/
+
+ iter->xhv_eiter = entry; /* HvEITER(hv) = entry */
return entry;
}
SV *
Perl_hv_iterkeysv(pTHX_ register HE *entry)
{
- if (HeKLEN(entry) != HEf_SVKEY) {
- HEK *hek = HeKEY_hek(entry);
- 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 {
- 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)));
}
/*
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;
}
}
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;
- I32 found = 0;
+ HE **first;
+ bool found = 0;
bool is_utf8 = FALSE;
int k_flags = 0;
const char *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(hv))[hash & (I32) HvMAX(hv)]; */
- oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
- 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 {
- int flags_masked = k_flags & HVhek_MASK;
- for (entry = *oentry; entry; i=0, oentry = &HeNEXT(entry), entry = *oentry) {
+ const int flags_masked = k_flags & HVhek_MASK;
+ 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)-- */
}
}
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);
}
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;
- int flags_masked = flags & HVhek_MASK;
+ const int flags_masked = flags & HVhek_MASK;
/* what follows is the moral equivalent of:
if (!(Svp = hv_fetch(PL_strtab, str, len, FALSE)))
hv_store(PL_strtab, str, len, Nullsv, hash);
+
+ 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(hv))[hash & (I32) HvMAX(hv)]; */
- oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
- for (entry = *oentry; entry; i=0, entry = HeNEXT(entry)) {
+ oentry = &(HvARRAY(PL_strtab))[hash & (I32) HvMAX(PL_strtab)];
+ 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);
+ /* 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.
+ */
+
+ New(0, 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)++ */
- if (xhv->xhv_keys > (IV)xhv->xhv_max /* HvKEYS(hv) > HvMAX(hv) */)
+ } else if (xhv->xhv_keys > (IV)xhv->xhv_max /* HvKEYS(hv) > HvMAX(hv) */) {
hsplit(PL_strtab);
}
}
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 * const 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 * const 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
+
+Check that a hash is in an internally consistent state.
+
+=cut
+*/
+
+void
+Perl_hv_assert(pTHX_ HV *hv)
+{
+ dVAR;
+ 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);
+
+ (void)hv_iterinit(hv);
+
+ while ((entry = hv_iternext_flags(hv, HV_ITERNEXT_WANTPLACEHOLDERS))) {
+ /* sanity check the values */
+ if (HeVAL(entry) == &PL_sv_placeholder) {
+ placeholders++;
+ } else {
+ real++;
+ }
+ /* sanity check the keys */
+ if (HeSVKEY(entry)) {
+ /* Don't know what to check on SV keys. */
+ } else if (HeKUTF8(entry)) {
+ withflags++;
+ if (HeKWASUTF8(entry)) {
+ PerlIO_printf(Perl_debug_log,
+ "hash key has both WASUFT8 and UTF8: '%.*s'\n",
+ (int) HeKLEN(entry), HeKEY(entry));
+ bad = 1;
+ }
+ } else if (HeKWASUTF8(entry)) {
+ withflags++;
+ }
+ }
+ if (!SvTIED_mg((SV*)hv, PERL_MAGIC_tied)) {
+ if (HvUSEDKEYS(hv) != real) {
+ PerlIO_printf(Perl_debug_log, "Count %d key(s), but hash reports %d\n",
+ (int) real, (int) HvUSEDKEYS(hv));
+ bad = 1;
+ }
+ if (HvPLACEHOLDERS_get(hv) != placeholders) {
+ PerlIO_printf(Perl_debug_log,
+ "Count %d placeholder(s), but hash reports %d\n",
+ (int) placeholders, (int) HvPLACEHOLDERS_get(hv));
+ 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((SV *)hv);
+ }
+ 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:
+ */