All for the price of a patch applicator! Buy now!
After a few more optimizations, I submit the attached patch for your
consideration (and enjoyment).
The patch as supplied seems give the best performance in my limited
tests. This does string-sharing for all hashes in the system, including for
symbol tables, literal keys, quoted keys, variable keys, and what have
you. This implementation does not have *any* extra cost associated with hash
FETCHes. Only STORE operations go through an extra hash lookup, and only
when the key is installed for the first time in the hash. The impact of this
is marginal, since the hash function is never computed more than once for
looking up the same string.
I have generalized the sharing of strings with a sharepvn() function that
could find use in other places in the source (in lieu of savepv() or
savepvn()). There is a complementary nosharepvn() function that is
applicable when refcounting. You can define REFCNT_STRTAB to do reference
counting of the strings in the table in order to free them when neccessary,
but I found that this only increases the run time without yielding any
significant memory savings. Without this define, any string, once installed
in the table, will have a full lifetime. There may be a case for
maintaining *two* string tables in the system, one refcounted and the other
not, but I haven't seen any test cases where this is warranted yet.
There is a new xhv_flags field (a U8) in the xpvhv structure that I have
used for the HVf_SHAREKEYS flag. (HVf_SVKEYS might soon be another). You
may explicitly mark a HV with the flag to enable/disable string sharing.
There a small change in the interface: the he_free() and he_delayfree()
functions now take an additional argument, a flag that specifies how the
entry must be destroyed. I could have made up two differently named clone
functions instead, but I think it is a good idea to add the flag parameter
for the future.
There are two additional functions to the interface to perl hashes:
HE *hv_fetch_ent(hv, key, klen, lval, hash);
HE *hv_store_ent(hv, key, klen, sv, hash);
Both return a hash entry structure when successful, which makes it possible
to carry the hash value around after calling one of these with a NULL hash
value.
And finally, you could define NODEFAULT_SHAREKEYS to turn off all sharing of
strings (essentially the perl of old).
I will post some excerpts from my bechmarking runs when I find more time
to compile something that's easier on the eyes.
Meanwhile, let me know if this works for you.
- Sarathy.
gsar@engin.umich.edu
P.S: Summary of my perl5 (patchlevel 2) configuration:
Platform:
osname=linux, osver=1, archname=i586-linux
uname='linux pujyam 1.2.13 #2 sun oct 8 17:33:58 edt 1995 i586 '
hint=recommended
Compiler:
cc='cc', optimize='-O -g', ld='cc'
cppflags='-DDEBUGGING -D__USE_BSD_SIGNAL -Dbool=char -DHAS_BOOL'
ccflags ='-DDEBUGGING -D__USE_BSD_SIGNAL -Dbool=char -DHAS_BOOL'
ldflags =' -L/usr/local/lib'
stdchar='char', d_stdstdio=define, usevfork=false
voidflags=15, castflags=0, d_casti32=undef, d_castneg=define
intsize=4, alignbytes=4, usemymalloc=n, randbits=31
Libraries:
so=so
libpth=/usr/local/lib /lib /usr/lib
libs=-lgdbm -ldbm -ldb -ldl -lm -lc -lbsd
libc=/usr/lib/libc.a
Dynamic Linking:
dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef
cccdlflags='-fpic', ccdlflags='-rdynamic', lddlflags='-shared
-L/usr/local/lib'
---------------------------------8<---------------------------------
*** perl.h.dist Wed Nov 15 17:13:16 1995
--- perl.h Wed Dec 20 05:01:43 1995
***************
*** 1282,1287 ****
--- 1282,1288 ----
IEXT AV * Iendav; /* names of END subroutines */
IEXT AV * Ipad; /* storage for lexically scoped temporaries */
IEXT AV * Ipadname; /* variable names for "my" variables */
+ IEXT HV * Istrtab; /* shared string table */
/* memory management */
IEXT SV ** Itmps_stack;
*** perl.c.dist Sun Nov 19 16:11:29 1995
--- perl.c Wed Dec 20 05:00:18 1995
***************
*** 1165,1170 ****
--- 1165,1173 ----
init_main_stash()
{
GV *gv;
+
+ strtab = newHV();
+ HvSHAREKEYS_off(strtab); /* mandatory */
curstash = defstash = newHV();
curstname = newSVpv("main",4);
gv = gv_fetchpv("main::",TRUE, SVt_PVHV);
*** interp.sym.dist Fri Nov 10 17:17:32 1995
--- interp.sym Wed Dec 20 04:59:38 1995
***************
*** 139,144 ****
--- 139,145 ----
statusvalue
stdingv
strchop
+ strtab
sv_count
sv_objcount
sv_root
*** util.c.dist Tue Nov 14 10:46:37 1995
--- util.c Wed Dec 20 04:57:58 1995
***************
*** 690,695 ****
--- 690,759 ----
return newaddr;
}
+ /* get a (constant) string ptr from the global string table */
+ /* string will get added if it is not already there */
+
+ char *
+ sharepvn(sv, len, hash)
+ char *sv;
+ I32 len;
+ register U32 hash;
+ {
+ register HE *entry;
+
+ if (!hash) {
+ register char *s;
+ register I32 i;
+ i = len;
+ s = sv;
+ while (i--)
+ hash = hash * 33 + *s++;
+ }
+
+ if (!((entry = hv_fetch_ent(strtab, sv, len, TRUE, hash)) ||
+ (entry = hv_store_ent(strtab, sv, len, Nullsv, hash))))
+ croak("panic: fetch from string table failed");
+ #ifdef REFCNT_STRTAB
+ if (entry->hent_val == Nullsv) { /* new entry */
+ entry->hent_val = (SV*)1; /* use value slot as REFCNT */
+ }
+ else
+ ++(I32)entry->hent_val;
+ #endif
+ return entry->hent_key;
+ }
+
+ /* possibly free a shared string if no one has access to it */
+
+ void
+ unsharepvn(sv, len, hash)
+ char *sv;
+ I32 len;
+ register U32 hash;
+ {
+ #ifdef REFCNT_STRTAB
+ register HE *entry;
+
+ if (!hash) {
+ register char *s;
+ register I32 i;
+ i = len;
+ s = sv;
+ while (i--)
+ hash = hash * 33 + *s++;
+ }
+ /* XXX could play perilous pointer games here instead */
+ if ((entry = hv_fetch_ent(strtab, sv, len, FALSE, hash))) {
+ if (!--(I32)entry->hent_val) {
+ entry->hent_val = 0;
+ hv_delete(strtab, sv, len, G_DISCARD);
+ }
+ }
+ else
+ warn("Attempt to free non-existent shared string");
+ #endif
+ }
+
#if !defined(I_STDARG) && !defined(I_VARARGS)
/*
*** global.sym.dist Wed Nov 15 14:58:14 1995
--- global.sym Wed Dec 20 05:32:03 1995
***************
*** 397,402 ****
--- 397,403 ----
hv_delete
hv_exists
hv_fetch
+ hv_fetch_ent
hv_stashpv
hv_iterinit
hv_iterkey
***************
*** 405,410 ****
--- 406,412 ----
hv_iterval
hv_magic
hv_store
+ hv_store_ent
hv_undef
ibcmp
ingroup
***************
*** 874,879 ****
--- 876,882 ----
run
savepv
savepvn
+ sharepvn
save_I32
save_aptr
save_ary
***************
*** 973,978 ****
--- 976,982 ----
taint_proper
too_few_arguments
too_many_arguments
+ unsharepvn
wait4pid
warn
watch
*** proto.h.dist Wed Nov 15 21:55:23 1995
--- proto.h Wed Dec 20 04:47:51 1995
***************
*** 128,140 ****
void gv_init _((GV *gv, HV *stash, char *name, STRLEN len, int multi));
HV* gv_stashpv _((char* name, I32 create));
HV* gv_stashsv _((SV* sv, I32 create));
! void he_delayfree _((HE* hent));
! void he_free _((HE* hent));
void hoistmust _((PMOP* pm));
void hv_clear _((HV* tb));
SV* hv_delete _((HV* tb, char* key, U32 klen, I32 flags));
bool hv_exists _((HV* tb, char* key, U32 klen));
SV** hv_fetch _((HV* tb, char* key, U32 klen, I32 lval));
I32 hv_iterinit _((HV* tb));
char* hv_iterkey _((HE* entry, I32* retlen));
HE* hv_iternext _((HV* tb));
--- 128,141 ----
void gv_init _((GV *gv, HV *stash, char *name, STRLEN len, int multi));
HV* gv_stashpv _((char* name, I32 create));
HV* gv_stashsv _((SV* sv, I32 create));
! void he_delayfree _((HE* hent, I32 shared));
! void he_free _((HE* hent, I32 shared));
void hoistmust _((PMOP* pm));
void hv_clear _((HV* tb));
SV* hv_delete _((HV* tb, char* key, U32 klen, I32 flags));
bool hv_exists _((HV* tb, char* key, U32 klen));
SV** hv_fetch _((HV* tb, char* key, U32 klen, I32 lval));
+ HE* hv_fetch_ent _((HV* tb, char* key, U32 klen, I32 lval, U32 hash));
I32 hv_iterinit _((HV* tb));
char* hv_iterkey _((HE* entry, I32* retlen));
HE* hv_iternext _((HV* tb));
***************
*** 142,147 ****
--- 143,149 ----
SV* hv_iterval _((HV* tb, HE* entry));
void hv_magic _((HV* hv, GV* gv, int how));
SV** hv_store _((HV* tb, char* key, U32 klen, SV* val, U32 hash));
+ HE* hv_store_ent _((HV* tb, char* key, U32 klen, SV* val, U32 hash));
void hv_undef _((HV* tb));
I32 ibcmp _((U8* a, U8* b, I32 len));
I32 ingroup _((I32 testgid, I32 effective));
***************
*** 356,361 ****
--- 358,365 ----
#endif
char* savepv _((char* sv));
char* savepvn _((char* sv, I32 len));
+ char* sharepvn _((char* sv, I32 len, U32 hash));
+ void unsharepvn _((char* sv, I32 len, U32 hash));
void savestack_grow _((void));
void save_aptr _((AV** aptr));
AV* save_ary _((GV* gv));
*** hv.c.dist Wed Nov 15 15:00:05 1995
--- hv.c Wed Dec 20 04:44:29 1995
***************
*** 127,132 ****
--- 127,217 ----
return 0;
}
+ /* returns a HE * structure with the all fields set */
+ /* note that hent_val will be a mortal sv for MAGICAL hashes */
+ HE *
+ hv_fetch_ent(hv,key,klen,lval,hash)
+ HV *hv;
+ char *key;
+ U32 klen;
+ I32 lval;
+ register U32 hash;
+ {
+ register XPVHV* xhv;
+ register char *s;
+ register I32 i;
+ register HE *entry;
+ SV *sv;
+
+ if (!hv)
+ return 0;
+
+ xhv = (XPVHV*)SvANY(hv);
+ if (!hash) {
+ i = klen;
+ hash = 0;
+ s = key;
+ while (i--)
+ hash = hash * 33 + *s++;
+ }
+
+ if (SvRMAGICAL(hv) && mg_find((SV*)hv,'P')) {
+ if (!(entry = xhv->xhv_eiter)) {
+ xhv->xhv_eiter = entry = new_he(); /* only one HE per MAGICAL hash */
+ Zero(entry, 1, HE);
+ }
+ sv = sv_newmortal();
+ mg_copy((SV*)hv, sv, key, klen);
+ entry->hent_val = sv;
+ if (HvSHAREKEYS(hv))
+ entry->hent_key = sharepvn(key,klen,hash);
+ else {
+ Safefree(entry->hent_key);
+ entry->hent_key = savepvn(key, klen);
+ }
+ entry->hent_klen = klen;
+ return entry;
+ }
+
+ if (!xhv->xhv_array) {
+ if (lval
+ #ifdef DYNAMIC_ENV_FETCH /* if it's an %ENV lookup, we may get it on the fly */
+ || (HvNAME(hv) && strEQ(HvNAME(hv),ENV_HV_NAME))
+ #endif
+ )
+ Newz(503,xhv->xhv_array, sizeof(HE*) * (xhv->xhv_max + 1), char);
+ else
+ return 0;
+ }
+
+ entry = ((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
+ for (; entry; entry = entry->hent_next) {
+ if (entry->hent_hash != hash) /* strings can't be equal */
+ continue;
+ if (entry->hent_klen != klen)
+ continue;
+ if (bcmp(entry->hent_key,key,klen)) /* is this it? */
+ continue;
+ return entry;
+ }
+ #ifdef DYNAMIC_ENV_FETCH /* %ENV lookup? If so, try to fetch the value now */
+ if (HvNAME(hv) && strEQ(HvNAME(hv),ENV_HV_NAME)) {
+ char *gotenv;
+
+ gotenv = my_getenv(key);
+ if (gotenv != NULL) {
+ sv = newSVpv(gotenv,strlen(gotenv));
+ return hv_store_ent(hv,key,klen,sv,hash);
+ }
+ }
+ #endif
+ if (lval) { /* gonna assign to this, so it better be there */
+ sv = NEWSV(61,0);
+ return hv_store_ent(hv,key,klen,sv,hash);
+ }
+ return 0;
+ }
+
SV**
hv_store(hv,key,klen,val,hash)
HV *hv;
***************
*** 183,189 ****
entry = new_he();
entry->hent_klen = klen;
! entry->hent_key = savepvn(key,klen);
entry->hent_val = val;
entry->hent_hash = hash;
entry->hent_next = *oentry;
--- 268,277 ----
entry = new_he();
entry->hent_klen = klen;
! if (HvSHAREKEYS(hv))
! entry->hent_key = sharepvn(key, klen, hash);
! else /* gotta do the real thing */
! entry->hent_key = savepvn(key,klen);
entry->hent_val = val;
entry->hent_hash = hash;
entry->hent_next = *oentry;
***************
*** 199,204 ****
--- 287,367 ----
return &entry->hent_val;
}
+ HE *
+ hv_store_ent(hv,key,klen,val,hash)
+ HV *hv;
+ char *key;
+ U32 klen;
+ SV *val;
+ register U32 hash;
+ {
+ register XPVHV* xhv;
+ register char *s;
+ register I32 i;
+ register HE *entry;
+ register HE **oentry;
+
+ if (!hv)
+ return 0;
+
+ xhv = (XPVHV*)SvANY(hv);
+ if (SvMAGICAL(hv)) {
+ mg_copy((SV*)hv, val, key, klen);
+ #ifndef OVERLOAD
+ if (!xhv->xhv_array)
+ return 0;
+ #else
+ if (!xhv->xhv_array && (SvMAGIC(hv)->mg_type != 'A'
+ || SvMAGIC(hv)->mg_moremagic))
+ return 0;
+ #endif /* OVERLOAD */
+ }
+ if (!hash) {
+ i = klen;
+ s = key;
+ while (i--)
+ hash = hash * 33 + *s++;
+ }
+
+ if (!xhv->xhv_array)
+ Newz(505, xhv->xhv_array, sizeof(HE**) * (xhv->xhv_max + 1), char);
+
+ oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
+ i = 1;
+
+ for (entry = *oentry; entry; i=0, entry = entry->hent_next) {
+ if (entry->hent_hash != hash) /* strings can't be equal */
+ continue;
+ if (entry->hent_klen != klen)
+ continue;
+ if (bcmp(entry->hent_key,key,klen)) /* is this it? */
+ continue;
+ SvREFCNT_dec(entry->hent_val);
+ entry->hent_val = val;
+ return entry;
+ }
+
+ entry = new_he();
+ entry->hent_klen = klen;
+ if (HvSHAREKEYS(hv))
+ entry->hent_key = sharepvn(key, klen, hash);
+ else /* gotta do the real thing */
+ entry->hent_key = savepvn(key,klen);
+ entry->hent_val = val;
+ entry->hent_hash = hash;
+ entry->hent_next = *oentry;
+ *oentry = entry;
+
+ xhv->xhv_keys++;
+ if (i) { /* initial entry? */
+ ++xhv->xhv_fill;
+ if (xhv->xhv_keys > xhv->xhv_max)
+ hsplit(hv);
+ }
+
+ return entry;
+ }
+
SV *
hv_delete(hv,key,klen,flags)
HV *hv;
***************
*** 253,259 ****
if (entry == xhv->xhv_eiter)
entry->hent_klen = -1;
else
! he_free(entry);
--xhv->xhv_keys;
return sv;
}
--- 416,422 ----
if (entry == xhv->xhv_eiter)
entry->hent_klen = -1;
else
! he_free(entry, HvSHAREKEYS(hv));
--xhv->xhv_keys;
return sv;
}
***************
*** 337,342 ****
--- 500,506 ----
assert(tmp >= newsize);
New(2,a, tmp, HE*);
Copy(xhv->xhv_array, a, oldsize, HE*);
+ #if 0
if (oldsize >= 64 && *(char*)&xhv->xnv_nv == 0) {
sv_add_arena((char*)xhv->xhv_array, oldsize * sizeof(HE*), 0);
sv_add_arena(((char*)a) + newsize * sizeof(HE*),
***************
*** 344,349 ****
--- 508,514 ----
SVf_FAKE);
}
else
+ #endif
Safefree(xhv->xhv_array);
#endif
***************
*** 384,389 ****
--- 549,557 ----
xhv = (XPVHV*)SvANY(hv);
SvPOK_off(hv);
SvNOK_off(hv);
+ #ifndef NODEFAULT_SHAREKEYS
+ HvSHAREKEYS_on(hv); /* key-sharing on by default */
+ #endif
xhv->xhv_max = 7; /* start with 8 buckets */
xhv->xhv_fill = 0;
xhv->xhv_pmroot = 0;
***************
*** 393,416 ****
}
void
! he_free(hent)
register HE *hent;
{
if (!hent)
return;
SvREFCNT_dec(hent->hent_val);
! Safefree(hent->hent_key);
del_he(hent);
}
void
! he_delayfree(hent)
register HE *hent;
{
if (!hent)
return;
sv_2mortal(hent->hent_val); /* free between statements */
! Safefree(hent->hent_key);
del_he(hent);
}
--- 561,600 ----
}
void
! he_free(hent, shared)
register HE *hent;
+ I32 shared;
{
if (!hent)
return;
SvREFCNT_dec(hent->hent_val);
! if (shared)
! #ifdef REFCNT_STRTAB
! unsharepvn(hent->hent_key, hent->hent_klen, hent->hent_hash);
! #else
! hent->hent_key = 0;
! #endif
! else
! Safefree(hent->hent_key);
del_he(hent);
}
void
! he_delayfree(hent, shared)
register HE *hent;
+ I32 shared;
{
if (!hent)
return;
sv_2mortal(hent->hent_val); /* free between statements */
! if (shared)
! #ifdef REFCNT_STRTAB
! unsharepvn(hent->hent_key, hent->hent_klen, hent->hent_hash);
! #else
! hent->hent_key = 0;
! #endif
! else
! Safefree(hent->hent_key);
del_he(hent);
}
***************
*** 441,446 ****
--- 625,631 ----
register HE *ohent = Null(HE*);
I32 riter;
I32 max;
+ I32 shared;
if (!hv)
return;
***************
*** 451,461 ****
max = HvMAX(hv);
array = HvARRAY(hv);
hent = array[0];
for (;;) {
if (hent) {
ohent = hent;
hent = hent->hent_next;
! he_free(ohent);
}
if (!hent) {
if (++riter > max)
--- 636,647 ----
max = HvMAX(hv);
array = HvARRAY(hv);
hent = array[0];
+ shared = HvSHAREKEYS(hv);
for (;;) {
if (hent) {
ohent = hent;
hent = hent->hent_next;
! he_free(ohent, shared);
}
if (!hent) {
if (++riter > max)
***************
*** 478,487 ****
--- 664,677 ----
#ifdef STRANGE_MALLOC
Safefree(xhv->xhv_array);
#else
+ #if 0
if (xhv->xhv_max < 127 || *(char*)&xhv->xnv_nv)
Safefree(xhv->xhv_array);
else /* We used last half, so use first half for SV arena too. */
sv_add_arena((char*)xhv->xhv_array, (xhv->xhv_max + 1) * sizeof(HE*),0);
+ #else
+ Safefree(xhv->xhv_array);
+ #endif
#endif
if (HvNAME(hv)) {
Safefree(HvNAME(hv));
***************
*** 504,510 ****
register XPVHV* xhv = (XPVHV*)SvANY(hv);
HE *entry = xhv->xhv_eiter;
if (entry && entry->hent_klen < 0) /* was deleted earlier? */
! he_free(entry);
xhv->xhv_riter = -1;
xhv->xhv_eiter = Null(HE*);
return xhv->xhv_fill;
--- 694,700 ----
register XPVHV* xhv = (XPVHV*)SvANY(hv);
HE *entry = xhv->xhv_eiter;
if (entry && entry->hent_klen < 0) /* was deleted earlier? */
! he_free(entry, HvSHAREKEYS(hv));
xhv->xhv_riter = -1;
xhv->xhv_eiter = Null(HE*);
return xhv->xhv_fill;
***************
*** 526,547 ****
if (SvRMAGICAL(hv) && (mg = mg_find((SV*)hv,'P'))) {
SV *key = sv_newmortal();
if (entry) {
! sv_usepvn(key, entry->hent_key, entry->hent_klen);
entry->hent_key = 0;
}
else {
! xhv->xhv_eiter = entry = new_he();
Zero(entry, 1, HE);
}
magic_nextpack((SV*) hv,mg,key);
if (SvOK(key)) {
STRLEN len;
! entry->hent_key = SvPV_force(key, len);
entry->hent_klen = len;
! SvPOK_off(key);
! SvPVX(key) = 0;
! return entry;
}
if (entry->hent_val)
SvREFCNT_dec(entry->hent_val);
--- 716,748 ----
if (SvRMAGICAL(hv) && (mg = mg_find((SV*)hv,'P'))) {
SV *key = sv_newmortal();
+ I32 shared = HvSHAREKEYS(hv);
if (entry) {
! if (shared)
! sv_setpvn(key, entry->hent_key, entry->hent_klen);
! else
! sv_usepvn(key, entry->hent_key, entry->hent_klen);
entry->hent_key = 0;
}
else {
! xhv->xhv_eiter = entry = new_he(); /* only one HE per MAGICAL hash */
Zero(entry, 1, HE);
}
magic_nextpack((SV*) hv,mg,key);
if (SvOK(key)) {
STRLEN len;
! char *ckey;
!
! ckey = SvPV_force(key, len);
! if (shared)
! entry->hent_key = sharepvn(ckey, len, 0);
! else {
! entry->hent_key = ckey;
! SvPOK_off(key);
! SvPVX(key) = 0;
! }
entry->hent_klen = len;
! return entry; /* note that hent_val is not set at all */
}
if (entry->hent_val)
SvREFCNT_dec(entry->hent_val);
***************
*** 566,572 ****
} while (!entry);
if (oldentry && oldentry->hent_klen < 0) /* was deleted earlier? */
! he_free(oldentry);
xhv->xhv_eiter = entry;
return entry;
--- 767,773 ----
} while (!entry);
if (oldentry && oldentry->hent_klen < 0) /* was deleted earlier? */
! he_free(oldentry, HvSHAREKEYS(hv));
xhv->xhv_eiter = entry;
return entry;
*** hv.h.dist Tue Oct 18 12:20:08 1994
--- hv.h Wed Dec 20 00:58:12 1995
***************
*** 30,37 ****
--- 30,41 ----
HE *xhv_eiter; /* current entry of iterator */
PMOP *xhv_pmroot; /* list of pm's for this package */
char *xhv_name; /* name, if a symbol table */
+ U8 xhv_flags;
};
+ #define HVf_SHAREKEYS 1 /* shares its (const) keys */
+ /*#define HVf_SVKEYS 2 *//* uses SV keys */
+
#define Nullhv Null(HV*)
#define HvARRAY(hv) ((HE**)((XPVHV*) SvANY(hv))->xhv_array)
#define HvFILL(hv) ((XPVHV*) SvANY(hv))->xhv_fill
***************
*** 41,46 ****
--- 45,56 ----
#define HvEITER(hv) ((XPVHV*) SvANY(hv))->xhv_eiter
#define HvPMROOT(hv) ((XPVHV*) SvANY(hv))->xhv_pmroot
#define HvNAME(hv) ((XPVHV*) SvANY(hv))->xhv_name
+
+ #define HvFLAGS(hv) ((XPVHV*) SvANY(hv))->xhv_flags
+
+ #define HvSHAREKEYS(hv) (HvFLAGS(hv) & HVf_SHAREKEYS)
+ #define HvSHAREKEYS_on(hv) (HvFLAGS(hv) |= HVf_SHAREKEYS)
+ #define HvSHAREKEYS_off(hv) (HvFLAGS(hv) &= ~HVf_SHAREKEYS)
#ifdef OVERLOAD
*** av.c.dist Wed Nov 15 14:26:17 1995
--- av.c Sat Dec 9 23:03:32 1995
***************
*** 82,90 ****
newmax = tmp - 1;
New(2,ary, newmax+1, SV*);
Copy(AvALLOC(av), ary, AvMAX(av)+1, SV*);
if (AvMAX(av) > 64 && !AvREUSED(av))
! sv_add_arena((char*)AvALLOC(av), AvMAX(av) * sizeof(SV*),0);
else
Safefree(AvALLOC(av));
AvALLOC(av) = ary;
#endif
--- 82,92 ----
newmax = tmp - 1;
New(2,ary, newmax+1, SV*);
Copy(AvALLOC(av), ary, AvMAX(av)+1, SV*);
+ #ifdef 0
if (AvMAX(av) > 64 && !AvREUSED(av))
! sv_add_arena((char*)AvALLOC(av), (AvMAX(av)+1) * sizeof(SV*),0);
else
+ #endif
Safefree(AvALLOC(av));
AvALLOC(av) = ary;
#endif
***************
*** 317,326 ****
key = AvFILL(av) + 1;
while (key)
SvREFCNT_dec(AvARRAY(av)[--key]);
- }
- if (key = AvARRAY(av) - AvALLOC(av)) {
- AvMAX(av) += key;
- SvPVX(av) = (char*)AvALLOC(av);
}
Safefree(AvALLOC(av));
AvALLOC(av) = 0;
--- 319,324 ----
*** av.h.dist Wed Nov 15 14:26:41 1995
--- av.h Sat Dec 9 18:45:40 1995
***************
*** 44,48 ****
#define AvREUSED_on(av) (AvFLAGS(av) |= AVf_REUSED)
#define AvREUSED_off(av) (AvFLAGS(av) &= ~AVf_REUSED)
! #define AvREALISH(av) AvFLAGS(av) /* REAL or REIFY -- shortcut */
--- 44,48 ----
#define AvREUSED_on(av) (AvFLAGS(av) |= AVf_REUSED)
#define AvREUSED_off(av) (AvFLAGS(av) &= ~AVf_REUSED)
! #define AvREALISH(av) (AvFLAGS(av) & (AVf_REAL|AVf_REIFY))
After a few more optimizations, I submit the attached patch for your
consideration (and enjoyment).
The patch as supplied seems give the best performance in my limited
tests. This does string-sharing for all hashes in the system, including for
symbol tables, literal keys, quoted keys, variable keys, and what have
you. This implementation does not have *any* extra cost associated with hash
FETCHes. Only STORE operations go through an extra hash lookup, and only
when the key is installed for the first time in the hash. The impact of this
is marginal, since the hash function is never computed more than once for
looking up the same string.
I have generalized the sharing of strings with a sharepvn() function that
could find use in other places in the source (in lieu of savepv() or
savepvn()). There is a complementary nosharepvn() function that is
applicable when refcounting. You can define REFCNT_STRTAB to do reference
counting of the strings in the table in order to free them when neccessary,
but I found that this only increases the run time without yielding any
significant memory savings. Without this define, any string, once installed
in the table, will have a full lifetime. There may be a case for
maintaining *two* string tables in the system, one refcounted and the other
not, but I haven't seen any test cases where this is warranted yet.
There is a new xhv_flags field (a U8) in the xpvhv structure that I have
used for the HVf_SHAREKEYS flag. (HVf_SVKEYS might soon be another). You
may explicitly mark a HV with the flag to enable/disable string sharing.
There a small change in the interface: the he_free() and he_delayfree()
functions now take an additional argument, a flag that specifies how the
entry must be destroyed. I could have made up two differently named clone
functions instead, but I think it is a good idea to add the flag parameter
for the future.
There are two additional functions to the interface to perl hashes:
HE *hv_fetch_ent(hv, key, klen, lval, hash);
HE *hv_store_ent(hv, key, klen, sv, hash);
Both return a hash entry structure when successful, which makes it possible
to carry the hash value around after calling one of these with a NULL hash
value.
And finally, you could define NODEFAULT_SHAREKEYS to turn off all sharing of
strings (essentially the perl of old).
I will post some excerpts from my bechmarking runs when I find more time
to compile something that's easier on the eyes.
Meanwhile, let me know if this works for you.
- Sarathy.
gsar@engin.umich.edu
P.S: Summary of my perl5 (patchlevel 2) configuration:
Platform:
osname=linux, osver=1, archname=i586-linux
uname='linux pujyam 1.2.13 #2 sun oct 8 17:33:58 edt 1995 i586 '
hint=recommended
Compiler:
cc='cc', optimize='-O -g', ld='cc'
cppflags='-DDEBUGGING -D__USE_BSD_SIGNAL -Dbool=char -DHAS_BOOL'
ccflags ='-DDEBUGGING -D__USE_BSD_SIGNAL -Dbool=char -DHAS_BOOL'
ldflags =' -L/usr/local/lib'
stdchar='char', d_stdstdio=define, usevfork=false
voidflags=15, castflags=0, d_casti32=undef, d_castneg=define
intsize=4, alignbytes=4, usemymalloc=n, randbits=31
Libraries:
so=so
libpth=/usr/local/lib /lib /usr/lib
libs=-lgdbm -ldbm -ldb -ldl -lm -lc -lbsd
libc=/usr/lib/libc.a
Dynamic Linking:
dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef
cccdlflags='-fpic', ccdlflags='-rdynamic', lddlflags='-shared
-L/usr/local/lib'
---------------------------------8<---------------------------------
*** perl.h.dist Wed Nov 15 17:13:16 1995
--- perl.h Wed Dec 20 05:01:43 1995
***************
*** 1282,1287 ****
--- 1282,1288 ----
IEXT AV * Iendav; /* names of END subroutines */
IEXT AV * Ipad; /* storage for lexically scoped temporaries */
IEXT AV * Ipadname; /* variable names for "my" variables */
+ IEXT HV * Istrtab; /* shared string table */
/* memory management */
IEXT SV ** Itmps_stack;
*** perl.c.dist Sun Nov 19 16:11:29 1995
--- perl.c Wed Dec 20 05:00:18 1995
***************
*** 1165,1170 ****
--- 1165,1173 ----
init_main_stash()
{
GV *gv;
+
+ strtab = newHV();
+ HvSHAREKEYS_off(strtab); /* mandatory */
curstash = defstash = newHV();
curstname = newSVpv("main",4);
gv = gv_fetchpv("main::",TRUE, SVt_PVHV);
*** interp.sym.dist Fri Nov 10 17:17:32 1995
--- interp.sym Wed Dec 20 04:59:38 1995
***************
*** 139,144 ****
--- 139,145 ----
statusvalue
stdingv
strchop
+ strtab
sv_count
sv_objcount
sv_root
*** util.c.dist Tue Nov 14 10:46:37 1995
--- util.c Wed Dec 20 04:57:58 1995
***************
*** 690,695 ****
--- 690,759 ----
return newaddr;
}
+ /* get a (constant) string ptr from the global string table */
+ /* string will get added if it is not already there */
+
+ char *
+ sharepvn(sv, len, hash)
+ char *sv;
+ I32 len;
+ register U32 hash;
+ {
+ register HE *entry;
+
+ if (!hash) {
+ register char *s;
+ register I32 i;
+ i = len;
+ s = sv;
+ while (i--)
+ hash = hash * 33 + *s++;
+ }
+
+ if (!((entry = hv_fetch_ent(strtab, sv, len, TRUE, hash)) ||
+ (entry = hv_store_ent(strtab, sv, len, Nullsv, hash))))
+ croak("panic: fetch from string table failed");
+ #ifdef REFCNT_STRTAB
+ if (entry->hent_val == Nullsv) { /* new entry */
+ entry->hent_val = (SV*)1; /* use value slot as REFCNT */
+ }
+ else
+ ++(I32)entry->hent_val;
+ #endif
+ return entry->hent_key;
+ }
+
+ /* possibly free a shared string if no one has access to it */
+
+ void
+ unsharepvn(sv, len, hash)
+ char *sv;
+ I32 len;
+ register U32 hash;
+ {
+ #ifdef REFCNT_STRTAB
+ register HE *entry;
+
+ if (!hash) {
+ register char *s;
+ register I32 i;
+ i = len;
+ s = sv;
+ while (i--)
+ hash = hash * 33 + *s++;
+ }
+ /* XXX could play perilous pointer games here instead */
+ if ((entry = hv_fetch_ent(strtab, sv, len, FALSE, hash))) {
+ if (!--(I32)entry->hent_val) {
+ entry->hent_val = 0;
+ hv_delete(strtab, sv, len, G_DISCARD);
+ }
+ }
+ else
+ warn("Attempt to free non-existent shared string");
+ #endif
+ }
+
#if !defined(I_STDARG) && !defined(I_VARARGS)
/*
*** global.sym.dist Wed Nov 15 14:58:14 1995
--- global.sym Wed Dec 20 05:32:03 1995
***************
*** 397,402 ****
--- 397,403 ----
hv_delete
hv_exists
hv_fetch
+ hv_fetch_ent
hv_stashpv
hv_iterinit
hv_iterkey
***************
*** 405,410 ****
--- 406,412 ----
hv_iterval
hv_magic
hv_store
+ hv_store_ent
hv_undef
ibcmp
ingroup
***************
*** 874,879 ****
--- 876,882 ----
run
savepv
savepvn
+ sharepvn
save_I32
save_aptr
save_ary
***************
*** 973,978 ****
--- 976,982 ----
taint_proper
too_few_arguments
too_many_arguments
+ unsharepvn
wait4pid
warn
watch
*** proto.h.dist Wed Nov 15 21:55:23 1995
--- proto.h Wed Dec 20 04:47:51 1995
***************
*** 128,140 ****
void gv_init _((GV *gv, HV *stash, char *name, STRLEN len, int multi));
HV* gv_stashpv _((char* name, I32 create));
HV* gv_stashsv _((SV* sv, I32 create));
! void he_delayfree _((HE* hent));
! void he_free _((HE* hent));
void hoistmust _((PMOP* pm));
void hv_clear _((HV* tb));
SV* hv_delete _((HV* tb, char* key, U32 klen, I32 flags));
bool hv_exists _((HV* tb, char* key, U32 klen));
SV** hv_fetch _((HV* tb, char* key, U32 klen, I32 lval));
I32 hv_iterinit _((HV* tb));
char* hv_iterkey _((HE* entry, I32* retlen));
HE* hv_iternext _((HV* tb));
--- 128,141 ----
void gv_init _((GV *gv, HV *stash, char *name, STRLEN len, int multi));
HV* gv_stashpv _((char* name, I32 create));
HV* gv_stashsv _((SV* sv, I32 create));
! void he_delayfree _((HE* hent, I32 shared));
! void he_free _((HE* hent, I32 shared));
void hoistmust _((PMOP* pm));
void hv_clear _((HV* tb));
SV* hv_delete _((HV* tb, char* key, U32 klen, I32 flags));
bool hv_exists _((HV* tb, char* key, U32 klen));
SV** hv_fetch _((HV* tb, char* key, U32 klen, I32 lval));
+ HE* hv_fetch_ent _((HV* tb, char* key, U32 klen, I32 lval, U32 hash));
I32 hv_iterinit _((HV* tb));
char* hv_iterkey _((HE* entry, I32* retlen));
HE* hv_iternext _((HV* tb));
***************
*** 142,147 ****
--- 143,149 ----
SV* hv_iterval _((HV* tb, HE* entry));
void hv_magic _((HV* hv, GV* gv, int how));
SV** hv_store _((HV* tb, char* key, U32 klen, SV* val, U32 hash));
+ HE* hv_store_ent _((HV* tb, char* key, U32 klen, SV* val, U32 hash));
void hv_undef _((HV* tb));
I32 ibcmp _((U8* a, U8* b, I32 len));
I32 ingroup _((I32 testgid, I32 effective));
***************
*** 356,361 ****
--- 358,365 ----
#endif
char* savepv _((char* sv));
char* savepvn _((char* sv, I32 len));
+ char* sharepvn _((char* sv, I32 len, U32 hash));
+ void unsharepvn _((char* sv, I32 len, U32 hash));
void savestack_grow _((void));
void save_aptr _((AV** aptr));
AV* save_ary _((GV* gv));
*** hv.c.dist Wed Nov 15 15:00:05 1995
--- hv.c Wed Dec 20 04:44:29 1995
***************
*** 127,132 ****
--- 127,217 ----
return 0;
}
+ /* returns a HE * structure with the all fields set */
+ /* note that hent_val will be a mortal sv for MAGICAL hashes */
+ HE *
+ hv_fetch_ent(hv,key,klen,lval,hash)
+ HV *hv;
+ char *key;
+ U32 klen;
+ I32 lval;
+ register U32 hash;
+ {
+ register XPVHV* xhv;
+ register char *s;
+ register I32 i;
+ register HE *entry;
+ SV *sv;
+
+ if (!hv)
+ return 0;
+
+ xhv = (XPVHV*)SvANY(hv);
+ if (!hash) {
+ i = klen;
+ hash = 0;
+ s = key;
+ while (i--)
+ hash = hash * 33 + *s++;
+ }
+
+ if (SvRMAGICAL(hv) && mg_find((SV*)hv,'P')) {
+ if (!(entry = xhv->xhv_eiter)) {
+ xhv->xhv_eiter = entry = new_he(); /* only one HE per MAGICAL hash */
+ Zero(entry, 1, HE);
+ }
+ sv = sv_newmortal();
+ mg_copy((SV*)hv, sv, key, klen);
+ entry->hent_val = sv;
+ if (HvSHAREKEYS(hv))
+ entry->hent_key = sharepvn(key,klen,hash);
+ else {
+ Safefree(entry->hent_key);
+ entry->hent_key = savepvn(key, klen);
+ }
+ entry->hent_klen = klen;
+ return entry;
+ }
+
+ if (!xhv->xhv_array) {
+ if (lval
+ #ifdef DYNAMIC_ENV_FETCH /* if it's an %ENV lookup, we may get it on the fly */
+ || (HvNAME(hv) && strEQ(HvNAME(hv),ENV_HV_NAME))
+ #endif
+ )
+ Newz(503,xhv->xhv_array, sizeof(HE*) * (xhv->xhv_max + 1), char);
+ else
+ return 0;
+ }
+
+ entry = ((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
+ for (; entry; entry = entry->hent_next) {
+ if (entry->hent_hash != hash) /* strings can't be equal */
+ continue;
+ if (entry->hent_klen != klen)
+ continue;
+ if (bcmp(entry->hent_key,key,klen)) /* is this it? */
+ continue;
+ return entry;
+ }
+ #ifdef DYNAMIC_ENV_FETCH /* %ENV lookup? If so, try to fetch the value now */
+ if (HvNAME(hv) && strEQ(HvNAME(hv),ENV_HV_NAME)) {
+ char *gotenv;
+
+ gotenv = my_getenv(key);
+ if (gotenv != NULL) {
+ sv = newSVpv(gotenv,strlen(gotenv));
+ return hv_store_ent(hv,key,klen,sv,hash);
+ }
+ }
+ #endif
+ if (lval) { /* gonna assign to this, so it better be there */
+ sv = NEWSV(61,0);
+ return hv_store_ent(hv,key,klen,sv,hash);
+ }
+ return 0;
+ }
+
SV**
hv_store(hv,key,klen,val,hash)
HV *hv;
***************
*** 183,189 ****
entry = new_he();
entry->hent_klen = klen;
! entry->hent_key = savepvn(key,klen);
entry->hent_val = val;
entry->hent_hash = hash;
entry->hent_next = *oentry;
--- 268,277 ----
entry = new_he();
entry->hent_klen = klen;
! if (HvSHAREKEYS(hv))
! entry->hent_key = sharepvn(key, klen, hash);
! else /* gotta do the real thing */
! entry->hent_key = savepvn(key,klen);
entry->hent_val = val;
entry->hent_hash = hash;
entry->hent_next = *oentry;
***************
*** 199,204 ****
--- 287,367 ----
return &entry->hent_val;
}
+ HE *
+ hv_store_ent(hv,key,klen,val,hash)
+ HV *hv;
+ char *key;
+ U32 klen;
+ SV *val;
+ register U32 hash;
+ {
+ register XPVHV* xhv;
+ register char *s;
+ register I32 i;
+ register HE *entry;
+ register HE **oentry;
+
+ if (!hv)
+ return 0;
+
+ xhv = (XPVHV*)SvANY(hv);
+ if (SvMAGICAL(hv)) {
+ mg_copy((SV*)hv, val, key, klen);
+ #ifndef OVERLOAD
+ if (!xhv->xhv_array)
+ return 0;
+ #else
+ if (!xhv->xhv_array && (SvMAGIC(hv)->mg_type != 'A'
+ || SvMAGIC(hv)->mg_moremagic))
+ return 0;
+ #endif /* OVERLOAD */
+ }
+ if (!hash) {
+ i = klen;
+ s = key;
+ while (i--)
+ hash = hash * 33 + *s++;
+ }
+
+ if (!xhv->xhv_array)
+ Newz(505, xhv->xhv_array, sizeof(HE**) * (xhv->xhv_max + 1), char);
+
+ oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
+ i = 1;
+
+ for (entry = *oentry; entry; i=0, entry = entry->hent_next) {
+ if (entry->hent_hash != hash) /* strings can't be equal */
+ continue;
+ if (entry->hent_klen != klen)
+ continue;
+ if (bcmp(entry->hent_key,key,klen)) /* is this it? */
+ continue;
+ SvREFCNT_dec(entry->hent_val);
+ entry->hent_val = val;
+ return entry;
+ }
+
+ entry = new_he();
+ entry->hent_klen = klen;
+ if (HvSHAREKEYS(hv))
+ entry->hent_key = sharepvn(key, klen, hash);
+ else /* gotta do the real thing */
+ entry->hent_key = savepvn(key,klen);
+ entry->hent_val = val;
+ entry->hent_hash = hash;
+ entry->hent_next = *oentry;
+ *oentry = entry;
+
+ xhv->xhv_keys++;
+ if (i) { /* initial entry? */
+ ++xhv->xhv_fill;
+ if (xhv->xhv_keys > xhv->xhv_max)
+ hsplit(hv);
+ }
+
+ return entry;
+ }
+
SV *
hv_delete(hv,key,klen,flags)
HV *hv;
***************
*** 253,259 ****
if (entry == xhv->xhv_eiter)
entry->hent_klen = -1;
else
! he_free(entry);
--xhv->xhv_keys;
return sv;
}
--- 416,422 ----
if (entry == xhv->xhv_eiter)
entry->hent_klen = -1;
else
! he_free(entry, HvSHAREKEYS(hv));
--xhv->xhv_keys;
return sv;
}
***************
*** 337,342 ****
--- 500,506 ----
assert(tmp >= newsize);
New(2,a, tmp, HE*);
Copy(xhv->xhv_array, a, oldsize, HE*);
+ #if 0
if (oldsize >= 64 && *(char*)&xhv->xnv_nv == 0) {
sv_add_arena((char*)xhv->xhv_array, oldsize * sizeof(HE*), 0);
sv_add_arena(((char*)a) + newsize * sizeof(HE*),
***************
*** 344,349 ****
--- 508,514 ----
SVf_FAKE);
}
else
+ #endif
Safefree(xhv->xhv_array);
#endif
***************
*** 384,389 ****
--- 549,557 ----
xhv = (XPVHV*)SvANY(hv);
SvPOK_off(hv);
SvNOK_off(hv);
+ #ifndef NODEFAULT_SHAREKEYS
+ HvSHAREKEYS_on(hv); /* key-sharing on by default */
+ #endif
xhv->xhv_max = 7; /* start with 8 buckets */
xhv->xhv_fill = 0;
xhv->xhv_pmroot = 0;
***************
*** 393,416 ****
}
void
! he_free(hent)
register HE *hent;
{
if (!hent)
return;
SvREFCNT_dec(hent->hent_val);
! Safefree(hent->hent_key);
del_he(hent);
}
void
! he_delayfree(hent)
register HE *hent;
{
if (!hent)
return;
sv_2mortal(hent->hent_val); /* free between statements */
! Safefree(hent->hent_key);
del_he(hent);
}
--- 561,600 ----
}
void
! he_free(hent, shared)
register HE *hent;
+ I32 shared;
{
if (!hent)
return;
SvREFCNT_dec(hent->hent_val);
! if (shared)
! #ifdef REFCNT_STRTAB
! unsharepvn(hent->hent_key, hent->hent_klen, hent->hent_hash);
! #else
! hent->hent_key = 0;
! #endif
! else
! Safefree(hent->hent_key);
del_he(hent);
}
void
! he_delayfree(hent, shared)
register HE *hent;
+ I32 shared;
{
if (!hent)
return;
sv_2mortal(hent->hent_val); /* free between statements */
! if (shared)
! #ifdef REFCNT_STRTAB
! unsharepvn(hent->hent_key, hent->hent_klen, hent->hent_hash);
! #else
! hent->hent_key = 0;
! #endif
! else
! Safefree(hent->hent_key);
del_he(hent);
}
***************
*** 441,446 ****
--- 625,631 ----
register HE *ohent = Null(HE*);
I32 riter;
I32 max;
+ I32 shared;
if (!hv)
return;
***************
*** 451,461 ****
max = HvMAX(hv);
array = HvARRAY(hv);
hent = array[0];
for (;;) {
if (hent) {
ohent = hent;
hent = hent->hent_next;
! he_free(ohent);
}
if (!hent) {
if (++riter > max)
--- 636,647 ----
max = HvMAX(hv);
array = HvARRAY(hv);
hent = array[0];
+ shared = HvSHAREKEYS(hv);
for (;;) {
if (hent) {
ohent = hent;
hent = hent->hent_next;
! he_free(ohent, shared);
}
if (!hent) {
if (++riter > max)
***************
*** 478,487 ****
--- 664,677 ----
#ifdef STRANGE_MALLOC
Safefree(xhv->xhv_array);
#else
+ #if 0
if (xhv->xhv_max < 127 || *(char*)&xhv->xnv_nv)
Safefree(xhv->xhv_array);
else /* We used last half, so use first half for SV arena too. */
sv_add_arena((char*)xhv->xhv_array, (xhv->xhv_max + 1) * sizeof(HE*),0);
+ #else
+ Safefree(xhv->xhv_array);
+ #endif
#endif
if (HvNAME(hv)) {
Safefree(HvNAME(hv));
***************
*** 504,510 ****
register XPVHV* xhv = (XPVHV*)SvANY(hv);
HE *entry = xhv->xhv_eiter;
if (entry && entry->hent_klen < 0) /* was deleted earlier? */
! he_free(entry);
xhv->xhv_riter = -1;
xhv->xhv_eiter = Null(HE*);
return xhv->xhv_fill;
--- 694,700 ----
register XPVHV* xhv = (XPVHV*)SvANY(hv);
HE *entry = xhv->xhv_eiter;
if (entry && entry->hent_klen < 0) /* was deleted earlier? */
! he_free(entry, HvSHAREKEYS(hv));
xhv->xhv_riter = -1;
xhv->xhv_eiter = Null(HE*);
return xhv->xhv_fill;
***************
*** 526,547 ****
if (SvRMAGICAL(hv) && (mg = mg_find((SV*)hv,'P'))) {
SV *key = sv_newmortal();
if (entry) {
! sv_usepvn(key, entry->hent_key, entry->hent_klen);
entry->hent_key = 0;
}
else {
! xhv->xhv_eiter = entry = new_he();
Zero(entry, 1, HE);
}
magic_nextpack((SV*) hv,mg,key);
if (SvOK(key)) {
STRLEN len;
! entry->hent_key = SvPV_force(key, len);
entry->hent_klen = len;
! SvPOK_off(key);
! SvPVX(key) = 0;
! return entry;
}
if (entry->hent_val)
SvREFCNT_dec(entry->hent_val);
--- 716,748 ----
if (SvRMAGICAL(hv) && (mg = mg_find((SV*)hv,'P'))) {
SV *key = sv_newmortal();
+ I32 shared = HvSHAREKEYS(hv);
if (entry) {
! if (shared)
! sv_setpvn(key, entry->hent_key, entry->hent_klen);
! else
! sv_usepvn(key, entry->hent_key, entry->hent_klen);
entry->hent_key = 0;
}
else {
! xhv->xhv_eiter = entry = new_he(); /* only one HE per MAGICAL hash */
Zero(entry, 1, HE);
}
magic_nextpack((SV*) hv,mg,key);
if (SvOK(key)) {
STRLEN len;
! char *ckey;
!
! ckey = SvPV_force(key, len);
! if (shared)
! entry->hent_key = sharepvn(ckey, len, 0);
! else {
! entry->hent_key = ckey;
! SvPOK_off(key);
! SvPVX(key) = 0;
! }
entry->hent_klen = len;
! return entry; /* note that hent_val is not set at all */
}
if (entry->hent_val)
SvREFCNT_dec(entry->hent_val);
***************
*** 566,572 ****
} while (!entry);
if (oldentry && oldentry->hent_klen < 0) /* was deleted earlier? */
! he_free(oldentry);
xhv->xhv_eiter = entry;
return entry;
--- 767,773 ----
} while (!entry);
if (oldentry && oldentry->hent_klen < 0) /* was deleted earlier? */
! he_free(oldentry, HvSHAREKEYS(hv));
xhv->xhv_eiter = entry;
return entry;
*** hv.h.dist Tue Oct 18 12:20:08 1994
--- hv.h Wed Dec 20 00:58:12 1995
***************
*** 30,37 ****
--- 30,41 ----
HE *xhv_eiter; /* current entry of iterator */
PMOP *xhv_pmroot; /* list of pm's for this package */
char *xhv_name; /* name, if a symbol table */
+ U8 xhv_flags;
};
+ #define HVf_SHAREKEYS 1 /* shares its (const) keys */
+ /*#define HVf_SVKEYS 2 *//* uses SV keys */
+
#define Nullhv Null(HV*)
#define HvARRAY(hv) ((HE**)((XPVHV*) SvANY(hv))->xhv_array)
#define HvFILL(hv) ((XPVHV*) SvANY(hv))->xhv_fill
***************
*** 41,46 ****
--- 45,56 ----
#define HvEITER(hv) ((XPVHV*) SvANY(hv))->xhv_eiter
#define HvPMROOT(hv) ((XPVHV*) SvANY(hv))->xhv_pmroot
#define HvNAME(hv) ((XPVHV*) SvANY(hv))->xhv_name
+
+ #define HvFLAGS(hv) ((XPVHV*) SvANY(hv))->xhv_flags
+
+ #define HvSHAREKEYS(hv) (HvFLAGS(hv) & HVf_SHAREKEYS)
+ #define HvSHAREKEYS_on(hv) (HvFLAGS(hv) |= HVf_SHAREKEYS)
+ #define HvSHAREKEYS_off(hv) (HvFLAGS(hv) &= ~HVf_SHAREKEYS)
#ifdef OVERLOAD
*** av.c.dist Wed Nov 15 14:26:17 1995
--- av.c Sat Dec 9 23:03:32 1995
***************
*** 82,90 ****
newmax = tmp - 1;
New(2,ary, newmax+1, SV*);
Copy(AvALLOC(av), ary, AvMAX(av)+1, SV*);
if (AvMAX(av) > 64 && !AvREUSED(av))
! sv_add_arena((char*)AvALLOC(av), AvMAX(av) * sizeof(SV*),0);
else
Safefree(AvALLOC(av));
AvALLOC(av) = ary;
#endif
--- 82,92 ----
newmax = tmp - 1;
New(2,ary, newmax+1, SV*);
Copy(AvALLOC(av), ary, AvMAX(av)+1, SV*);
+ #ifdef 0
if (AvMAX(av) > 64 && !AvREUSED(av))
! sv_add_arena((char*)AvALLOC(av), (AvMAX(av)+1) * sizeof(SV*),0);
else
+ #endif
Safefree(AvALLOC(av));
AvALLOC(av) = ary;
#endif
***************
*** 317,326 ****
key = AvFILL(av) + 1;
while (key)
SvREFCNT_dec(AvARRAY(av)[--key]);
- }
- if (key = AvARRAY(av) - AvALLOC(av)) {
- AvMAX(av) += key;
- SvPVX(av) = (char*)AvALLOC(av);
}
Safefree(AvALLOC(av));
AvALLOC(av) = 0;
--- 319,324 ----
*** av.h.dist Wed Nov 15 14:26:41 1995
--- av.h Sat Dec 9 18:45:40 1995
***************
*** 44,48 ****
#define AvREUSED_on(av) (AvFLAGS(av) |= AVf_REUSED)
#define AvREUSED_off(av) (AvFLAGS(av) &= ~AVf_REUSED)
! #define AvREALISH(av) AvFLAGS(av) /* REAL or REIFY -- shortcut */
--- 44,48 ----
#define AvREUSED_on(av) (AvFLAGS(av) |= AVf_REUSED)
#define AvREUSED_off(av) (AvFLAGS(av) &= ~AVf_REUSED)
! #define AvREALISH(av) (AvFLAGS(av) & (AVf_REAL|AVf_REIFY))