stas 2004/07/14 23:23:21
Modified: src/modules/perl modperl_common_util.c modperl_common_util.h
t/lib/TestAPRlib table.pm
xs/APR/Table APR__Table.h
xs/maps apr_functions.map
xs/tables/current/ModPerl FunctionTable.pm
. Changes
Log:
fix an old outstanding bug in the APR::Table's TIE interface with
each()/values() over tables with multi-values keys. Now the produced
order is correct and consistent with keys(). Though, values() works
correctly only with perl 5.8.x and higher.
Submitted by: Joe Schaefer
Revision Changes Path
1.3 +64 -6 modperl-2.0/src/modules/perl/modperl_common_util.c
Index: modperl_common_util.c
===================================================================
RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_common_util.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -u -r1.2 -r1.3
--- modperl_common_util.c 22 Jun 2004 22:34:10 -0000 1.2
+++ modperl_common_util.c 15 Jul 2004 06:23:20 -0000 1.3
@@ -22,6 +22,41 @@
#include "modperl_common_util.h"
+
+/* Prefetch magic requires perl 5.8 */
+#if ((PERL_REVISION == 5) && (PERL_VERSION >= 8))
+
+/* A custom MGVTBL with mg_copy slot filled in allows us to FETCH a
+ * table entry immediately during iteration. For multivalued keys
+ * this is essential in order to get the value corresponding to the
+ * current key, otherwise values() will always report the first value
+ * repeatedly. With this MGVTBL the keys() list always matches up
+ * with the values() list, even in the multivalued case. We only
+ * prefetch the value during iteration, because the prefetch adds
+ * overhead (an unnecessary FETCH call) to EXISTS and STORE
+ * operations. This way they are only "penalized" when the perl
+ * program is iterating via each(), which seems to be a reasonable
+ * tradeoff.
+ */
+
+MP_INLINE static
+int modperl_table_magic_copy(pTHX_ SV *sv, MAGIC *mg, SV *nsv,
+ const char *name, int namelen)
+{
+ /* prefetch the value whenever we're iterating over the keys */
+ MAGIC *tie_magic = mg_find(nsv, PERL_MAGIC_tiedelem);
+ SV *obj = SvRV(tie_magic->mg_obj);
+ if (SvCUR(obj)) {
+ SvGETMAGIC(nsv);
+ }
+ return 0;
+}
+
+
+static const MGVTBL modperl_table_magic_prefetch = {0, 0, 0, 0, 0,
+ modperl_table_magic_copy};
+#endif /* End of prefetch magic */
+
MP_INLINE SV *modperl_hash_tie(pTHX_
const char *classname,
SV *tsv, void *p)
@@ -30,15 +65,25 @@
SV *rsv = sv_newmortal();
sv_setref_pv(rsv, classname, p);
+
+ /* Prefetch magic requires perl 5.8 */
+#if ((PERL_REVISION == 5) && (PERL_VERSION >= 8))
+
+ sv_magic(hv, NULL, PERL_MAGIC_ext, Nullch, -1);
+ SvMAGIC(hv)->mg_virtual = (MGVTBL *)&modperl_table_magic_prefetch;
+ SvMAGIC(hv)->mg_flags |= MGf_COPY;
+
+#endif /* End of prefetch magic */
+
sv_magic(hv, rsv, PERL_MAGIC_tied, Nullch, 0);
return SvREFCNT_inc(sv_bless(sv_2mortal(newRV_noinc(hv)),
gv_stashpv(classname, TRUE)));
}
-MP_INLINE void *modperl_hash_tied_object(pTHX_
- const char *classname,
- SV *tsv)
+MP_INLINE SV *modperl_hash_tied_object_rv(pTHX_
+ const char *classname,
+ SV *tsv)
{
if (sv_derived_from(tsv, classname)) {
if (SVt_PVHV == SvTYPE(SvRV(tsv))) {
@@ -47,7 +92,7 @@
if (SvMAGICAL(hv)) {
if ((mg = mg_find(hv, PERL_MAGIC_tied))) {
- return (void *)MgObjIV(mg);
+ return mg->mg_obj;
}
else {
Perl_warn(aTHX_ "Not a tied hash: (magic=%c)", mg);
@@ -58,7 +103,7 @@
}
}
else {
- return (void *)SvObjIV(tsv);
+ return tsv;
}
}
else {
@@ -67,7 +112,20 @@
"(expecting an %s derived object)", classname);
}
- return NULL;
+ return &PL_sv_undef;
+}
+
+MP_INLINE void *modperl_hash_tied_object(pTHX_
+ const char *classname,
+ SV *tsv)
+{
+ SV *rv = modperl_hash_tied_object_rv(aTHX_ classname, tsv);
+ if (SvROK(rv)) {
+ return (void *)SvIVX(SvRV(rv));
+ }
+ else {
+ return NULL;
+ }
}
/* same as Symbol::gensym() */
1.3 +4 -0 modperl-2.0/src/modules/perl/modperl_common_util.h
Index: modperl_common_util.h
===================================================================
RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_common_util.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -u -r1.2 -r1.3
--- modperl_common_util.h 22 Jun 2004 22:34:10 -0000 1.2
+++ modperl_common_util.h 15 Jul 2004 06:23:20 -0000 1.3
@@ -75,6 +75,10 @@
SV *tsv, void *p);
/* tied %hash */
+MP_INLINE SV *modperl_hash_tied_object_rv(pTHX_
+ const char *classname,
+ SV *tsv);
+/* tied %hash */
MP_INLINE void *modperl_hash_tied_object(pTHX_ const char *classname,
SV *tsv);
1.5 +39 -2 modperl-2.0/t/lib/TestAPRlib/table.pm
Index: table.pm
===================================================================
RCS file: /home/cvs/modperl-2.0/t/lib/TestAPRlib/table.pm,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -u -r1.4 -r1.5
--- table.pm 15 Jul 2004 05:42:04 -0000 1.4
+++ table.pm 15 Jul 2004 06:23:21 -0000 1.5
@@ -11,13 +11,19 @@
use APR::Table ();
use APR::Pool ();
-use APR::Const -compile => ':table';
+use APR::Const -compile => ':table';
use constant TABLE_SIZE => 20;
our $filter_count;
sub num_of_tests {
- return 38;
+ my $tests = 50;
+
+ # tied hash values() for a table w/ multiple values for the same
+ # key
+ $tests += 2 if $] >= 5.008;
+
+ return $tests;
}
sub test {
@@ -173,6 +179,37 @@
my_filter($key, $table->{$key});
}
ok $filter_count == TABLE_SIZE;
+ }
+
+
+ # each, values
+ {
+ my $table = APR::Table::make($pool, 2);
+
+ $table->add("first" => 1);
+ $table->add("second" => 2);
+ $table->add("first" => 3);
+
+ my $i = 0;
+ while (my($a,$b) = each %$table) {
+ my $key = ("first", "second")[$i % 2];
+ my $val = ++$i;
+
+ ok t_cmp $a, $key, "table each: key test";
+ ok t_cmp $b, $val, "table each: value test";
+ ok t_cmp $table->{$a}, $val, "table each: get test";
+
+ ok t_cmp tied(%$table)->FETCH($a), $val,
+ "table each: tied get test";
+ }
+
+ # this doesn't work with Perl < 5.8
+ if ($] >= 5.008) {
+ ok t_cmp "1,2,3", join(",", values %$table),
+ "table values";
+ ok t_cmp "first,1,second,2,first,3", join(",", %$table),
+ "table entries";
+ }
}
# overlap and compress routines
1.12 +38 -12 modperl-2.0/xs/APR/Table/APR__Table.h
Index: APR__Table.h
===================================================================
RCS file: /home/cvs/modperl-2.0/xs/APR/Table/APR__Table.h,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -u -r1.11 -r1.12
--- APR__Table.h 4 Mar 2004 06:01:10 -0000 1.11
+++ APR__Table.h 15 Jul 2004 06:23:21 -0000 1.12
@@ -13,7 +13,6 @@
* limitations under the License.
*/
-#define mpxs_APR__Table_FETCH apr_table_get
#define mpxs_APR__Table_STORE apr_table_set
#define mpxs_APR__Table_DELETE apr_table_unset
#define mpxs_APR__Table_CLEAR apr_table_clear
@@ -122,26 +121,53 @@
static MP_INLINE const char *mpxs_APR__Table_NEXTKEY(pTHX_ SV *tsv, SV *key)
{
- apr_table_t *t = mp_xs_sv2_APR__Table(tsv);
+ apr_table_t *t;
+ SV *rv = modperl_hash_tied_object_rv(aTHX_ "APR::Table", tsv);
+ if (!SvROK(rv)) {
+ Perl_croak(aTHX_ "Usage: $table->NEXTKEY($key): "
+ "first argument not an APR::Table object");
+ }
+
+ t = (apr_table_t *)SvIVX(SvRV(rv));
if (apr_is_empty_table(t)) {
return NULL;
}
- if (mpxs_apr_table_iterix(tsv) < apr_table_elts(t)->nelts) {
- return mpxs_apr_table_nextkey(t, tsv);
+ if (key == NULL) {
+ mpxs_apr_table_iterix(rv) = 0; /* reset iterator index */
+ }
+
+ if (mpxs_apr_table_iterix(rv) < apr_table_elts(t)->nelts) {
+ return mpxs_apr_table_nextkey(t, rv);
}
+ mpxs_apr_table_iterix(rv) = 0;
+
return NULL;
}
-static MP_INLINE const char *mpxs_APR__Table_FIRSTKEY(pTHX_ SV *tsv)
-{
- mpxs_apr_table_iterix(tsv) = 0; /* reset iterator index */
+/* Try to shortcut apr_table_get by fetching the key using the current
+ * iterator (unless it's inactive or points at different key).
+ */
+static MP_INLINE const char *mpxs_APR__Table_FETCH(pTHX_ SV *tsv,
+ const char *key)
+{
+ SV* rv = modperl_hash_tied_object_rv(aTHX_ "APR::Table", tsv);
+ const int i = mpxs_apr_table_iterix(rv);
+ apr_table_t *t = (apr_table_t *)SvIVX(SvRV(rv));
+ const apr_array_header_t *arr = apr_table_elts(t);
+ apr_table_entry_t *elts = (apr_table_entry_t *)arr->elts;
- return mpxs_APR__Table_NEXTKEY(aTHX_ tsv, Nullsv);
+ if (i > 0 && i <= arr->nelts && !strcasecmp(key, elts[i-1].key)) {
+ return elts[i-1].val;
+ }
+ else {
+ return apr_table_get(t, key);
+ }
}
+
static XS(MPXS_apr_table_get)
{
dXSARGS;
@@ -153,11 +179,11 @@
mpxs_PPCODE({
APR__Table t = modperl_hash_tied_object(aTHX_ "APR::Table", ST(0));
const char *key = (const char *)SvPV_nolen(ST(1));
-
+
if (!t) {
XSRETURN_UNDEF;
}
-
+
if (GIMME_V == G_SCALAR) {
const char *val = apr_table_get(t, key);
@@ -167,9 +193,9 @@
}
else {
const apr_array_header_t *arr = apr_table_elts(t);
- apr_table_entry_t *elts = (apr_table_entry_t *)arr->elts;
+ apr_table_entry_t *elts = (apr_table_entry_t *)arr->elts;
int i;
-
+
for (i = 0; i < arr->nelts; i++) {
if (!elts[i].key || strcasecmp(elts[i].key, key)) {
continue;
1.83 +3 -3 modperl-2.0/xs/maps/apr_functions.map
Index: apr_functions.map
===================================================================
RCS file: /home/cvs/modperl-2.0/xs/maps/apr_functions.map,v
retrieving revision 1.82
retrieving revision 1.83
diff -u -u -r1.82 -r1.83
--- apr_functions.map 9 Jun 2004 14:46:22 -0000 1.82
+++ apr_functions.map 15 Jul 2004 06:23:21 -0000 1.83
@@ -253,12 +253,12 @@
-apr_table_setn
apr_table_unset
-apr_table_vdo
- const char *:DEFINE_FETCH | | apr_table_t *:t, const char *:key
void:DEFINE_STORE | | apr_table_t *:t, const char *:key, const char *:value
void:DEFINE_DELETE | | apr_table_t *:t, const char *:key
void:DEFINE_CLEAR | | apr_table_t *:t
- mpxs_APR__Table_FIRSTKEY
- mpxs_APR__Table_NEXTKEY
+ const char *:DEFINE_FIRSTKEY | mpxs_APR__Table_NEXTKEY | SV *:tsv, SV *:key=Nullsv
+ mpxs_APR__Table_NEXTKEY | | SV *:tsv, SV *:key=&PL_sv_undef
+ mpxs_APR__Table_FETCH
mpxs_APR__Table_EXISTS
!MODULE=APR::File
1.172 +27 -2 modperl-2.0/xs/tables/current/ModPerl/FunctionTable.pm
Index: FunctionTable.pm
===================================================================
RCS file: /home/cvs/modperl-2.0/xs/tables/current/ModPerl/FunctionTable.pm,v
retrieving revision 1.171
retrieving revision 1.172
diff -u -u -r1.171 -r1.172
--- FunctionTable.pm 12 Jul 2004 08:19:40 -0000 1.171
+++ FunctionTable.pm 15 Jul 2004 06:23:21 -0000 1.172
@@ -2,7 +2,7 @@
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# ! WARNING: generated by ModPerl::ParseSource/0.01
-# ! Mon Jul 12 00:41:18 2004
+# ! Wed Jul 14 22:25:32 2004
# ! do NOT edit, any changes will be lost !
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@@ -2619,6 +2619,27 @@
]
},
{
+ 'return_type' => 'SV *',
+ 'name' => 'modperl_hash_tied_object_rv',
+ 'attr' => [
+ '__inline__'
+ ],
+ 'args' => [
+ {
+ 'type' => 'PerlInterpreter *',
+ 'name' => 'my_perl'
+ },
+ {
+ 'type' => 'const char *',
+ 'name' => 'classname'
+ },
+ {
+ 'type' => 'SV *',
+ 'name' => 'tsv'
+ }
+ ]
+ },
+ {
'return_type' => 'int',
'name' => 'modperl_header_parser_handler',
'args' => [
@@ -5598,7 +5619,7 @@
},
{
'return_type' => 'const char *',
- 'name' => 'mpxs_APR__Table_FIRSTKEY',
+ 'name' => 'mpxs_APR__Table_FETCH',
'attr' => [
'static',
'__inline__'
@@ -5611,6 +5632,10 @@
{
'type' => 'SV *',
'name' => 'tsv'
+ },
+ {
+ 'type' => 'const char *',
+ 'name' => 'key'
}
]
},
1.413 +5 -0 modperl-2.0/Changes
Index: Changes
===================================================================
RCS file: /home/cvs/modperl-2.0/Changes,v
retrieving revision 1.412
retrieving revision 1.413
diff -u -u -r1.412 -r1.413
--- Changes 15 Jul 2004 05:40:14 -0000 1.412
+++ Changes 15 Jul 2004 06:23:21 -0000 1.413
@@ -12,6 +12,11 @@
=item 1.99_15-dev
+fix an old outstanding bug in the APR::Table's TIE interface with
+each()/values() over tables with multi-values keys. Now the produced
+order is correct and consistent with keys(). Though, values() works
+correctly only with perl 5.8.x and higher. [Joe Schaefer]
+
require Perl 5.6.1, 5.6.0 isn't supported for a long time, but we
weren't aborting at the Makefile.PL stage [Stas]