1 /*
2 **  Licensed to the Apache Software Foundation (ASF) under one or more
3 ** contributor license agreements.  See the NOTICE file distributed with
4 ** this work for additional information regarding copyright ownership.
5 ** The ASF licenses this file to You under the Apache License, Version 2.0
6 ** (the "License"); you may not use this file except in compliance with
7 ** the License.  You may obtain a copy of the License at
8 **
9 **      http://www.apache.org/licenses/LICENSE-2.0
10 **
11 **  Unless required by applicable law or agreed to in writing, software
12 **  distributed under the License is distributed on an "AS IS" BASIS,
13 **  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 **  See the License for the specific language governing permissions and
15 **  limitations under the License.
16 */
17 
18 #ifndef APREQ_XS_TABLES_H
19 #define APREQ_XS_TABLES_H
20 
21 /* backward compatibility macros support */
22 
23 #include "ppport.h"
24 
25 /**************************************************/
26 
27 
28 #if (PERL_VERSION >= 8) /* MAGIC ITERATOR REQUIRES 5.8 */
29 
30 /* Requires perl 5.8 or better.
31  * A custom MGVTBL with its "copy" slot filled allows
32  * us to FETCH a table entry immediately during iteration.
33  * For multivalued keys this is essential in order to get
34  * the value corresponding to the current key, otherwise
35  * values() will always report the first value repeatedly.
36  * With this MGVTBL the keys() list always matches up with
37  * the values() list, even in the multivalued case.
38  * We only prefetch the value during iteration, because the
39  * prefetch adds overhead to EXISTS and STORE operations.
40  * They are only "penalized" when the perl program is iterating
41  * via each(), which seems to be a reasonable tradeoff.
42  */
43 
apreq_xs_cookie_table_magic_copy(pTHX_ SV * sv,MAGIC * mg,SV * nsv,const char * name,int namelen)44 static int apreq_xs_cookie_table_magic_copy(pTHX_ SV *sv, MAGIC *mg, SV *nsv,
45                                             const char *name, int namelen)
46 {
47     /* Prefetch the value whenever the table iterator is > 0 */
48     MAGIC *tie_magic = mg_find(nsv, PERL_MAGIC_tiedelem);
49     SV *obj = SvRV(tie_magic->mg_obj);
50     IV idx = SvIVX(obj);
51     const apr_table_t *t = INT2PTR(apr_table_t *, idx);
52     const apr_array_header_t *arr = apr_table_elts(t);
53 
54     idx = SvCUR(obj);
55 
56     if (idx > 0 && idx <= arr->nelts) {
57         const apr_table_entry_t *te = (const apr_table_entry_t *)arr->elts;
58         apreq_cookie_t *c = apreq_value_to_cookie(te[idx-1].val);
59         MAGIC *my_magic = mg_find(obj, PERL_MAGIC_ext);
60 
61         SvMAGICAL_off(nsv);
62         sv_setsv(nsv, sv_2mortal(apreq_xs_cookie2sv(aTHX_ c, my_magic->mg_ptr,
63                                                     my_magic->mg_obj)));
64     }
65 
66     return 0;
67 }
68 
69 static const MGVTBL apreq_xs_cookie_table_magic = {0, 0, 0, 0, 0,
70                                 apreq_xs_cookie_table_magic_copy};
71 
72 #endif
73 
74 static APR_INLINE
apreq_xs_cookie_table2sv(pTHX_ const apr_table_t * t,const char * class,SV * parent,const char * value_class,I32 vclen)75 SV *apreq_xs_cookie_table2sv(pTHX_ const apr_table_t *t, const char *class, SV *parent,
76                       const char *value_class, I32 vclen)
77 {
78     SV *sv = (SV *)newHV();
79     SV *rv = sv_setref_pv(newSV(0), class, (void *)t);
80     sv_magic(SvRV(rv), parent, PERL_MAGIC_ext, value_class, vclen);
81 
82 #if (PERL_VERSION >= 8) /* MAGIC ITERATOR requires 5.8 */
83 
84     sv_magic(sv, NULL, PERL_MAGIC_ext, Nullch, -1);
85     SvMAGIC(sv)->mg_virtual = (MGVTBL *)&apreq_xs_cookie_table_magic;
86     SvMAGIC(sv)->mg_flags |= MGf_COPY;
87 
88 #endif
89 
90     sv_magic(sv, rv, PERL_MAGIC_tied, Nullch, 0);
91     SvREFCNT_dec(rv); /* corrects SvREFCNT_inc(rv) implicit in sv_magic */
92 
93     return sv_bless(newRV_noinc(sv), SvSTASH(SvRV(rv)));
94 }
95 
96 
97 
apreq_xs_cookie_table_keys(void * data,const char * key,const char * val)98 static int apreq_xs_cookie_table_keys(void *data, const char *key,
99                                       const char *val)
100 {
101 #ifdef USE_ITHREADS
102     struct apreq_xs_do_arg *d = (struct apreq_xs_do_arg *)data;
103     dTHXa(d->perl);
104 #endif
105     dSP;
106     apreq_cookie_t *c = apreq_value_to_cookie(val);
107     SV *sv = newSVpvn(key, c->v.nlen);
108     if (apreq_cookie_is_tainted(c))
109         SvTAINTED_on(sv);
110 
111 #ifndef USE_ITHREADS
112 		(void)data;
113 #endif
114     XPUSHs(sv_2mortal(sv));
115     PUTBACK;
116     return 1;
117 }
118 
apreq_xs_cookie_table_values(void * data,const char * key,const char * val)119 static int apreq_xs_cookie_table_values(void *data, const char *key,
120                                         const char *val)
121 {
122     struct apreq_xs_do_arg *d = (struct apreq_xs_do_arg *)data;
123     dTHXa(d->perl);
124     dSP;
125     apreq_cookie_t *c = apreq_value_to_cookie(val);
126     SV *sv = apreq_xs_cookie2sv(aTHX_ c, d->pkg, d->parent);
127 
128     XPUSHs(sv_2mortal(sv));
129     PUTBACK;
130     return 1;
131 }
132 
133 
134 /**************************************************/
135 
136 
137 #if (PERL_VERSION >= 8) /* MAGIC ITERATOR REQUIRES 5.8 */
138 
139 /* Requires perl 5.8 or better.
140  * A custom MGVTBL with its "copy" slot filled allows
141  * us to FETCH a table entry immediately during iteration.
142  * For multivalued keys this is essential in order to get
143  * the value corresponding to the current key, otherwise
144  * values() will always report the first value repeatedly.
145  * With this MGVTBL the keys() list always matches up with
146  * the values() list, even in the multivalued case.
147  * We only prefetch the value during iteration, because the
148  * prefetch adds overhead to EXISTS and STORE operations.
149  * They are only "penalized" when the perl program is iterating
150  * via each(), which seems to be a reasonable tradeoff.
151  */
152 
apreq_xs_param_table_magic_copy(pTHX_ SV * sv,MAGIC * mg,SV * nsv,const char * name,int namelen)153 static int apreq_xs_param_table_magic_copy(pTHX_ SV *sv, MAGIC *mg, SV *nsv,
154                                   const char *name, int namelen)
155 {
156     /* Prefetch the value whenever the table iterator is > 0 */
157     MAGIC *tie_magic = mg_find(nsv, PERL_MAGIC_tiedelem);
158     SV *obj = SvRV(tie_magic->mg_obj);
159     IV idx = SvIVX(obj);
160     const apr_table_t *t = INT2PTR(apr_table_t *, idx);
161     const apr_array_header_t *arr = apr_table_elts(t);
162 
163     idx = SvCUR(obj);
164 
165     if (idx > 0 && idx <= arr->nelts) {
166         const apr_table_entry_t *te = (const apr_table_entry_t *)arr->elts;
167         apreq_param_t *p = apreq_value_to_param(te[idx-1].val);
168         MAGIC *my_magic = mg_find(obj, PERL_MAGIC_ext);
169 
170         SvMAGICAL_off(nsv);
171         sv_setsv(nsv, sv_2mortal(apreq_xs_param2sv(aTHX_ p, my_magic->mg_ptr,
172                                                    my_magic->mg_obj)));
173     }
174 
175     return 0;
176 }
177 
178 static const MGVTBL apreq_xs_param_table_magic = {0, 0, 0, 0, 0,
179                                  apreq_xs_param_table_magic_copy};
180 
181 #endif
182 
183 static APR_INLINE
apreq_xs_param_table2sv(pTHX_ const apr_table_t * t,const char * class,SV * parent,const char * value_class,I32 vclen)184 SV *apreq_xs_param_table2sv(pTHX_ const apr_table_t *t, const char *class, SV *parent,
185                             const char *value_class, I32 vclen)
186 {
187     SV *sv = (SV *)newHV();
188     SV *rv = sv_setref_pv(newSV(0), class, (void *)t);
189     sv_magic(SvRV(rv), parent, PERL_MAGIC_ext, value_class, vclen);
190 
191 #if (PERL_VERSION >= 8) /* MAGIC ITERATOR requires 5.8 */
192 
193     sv_magic(sv, NULL, PERL_MAGIC_ext, Nullch, -1);
194     SvMAGIC(sv)->mg_virtual = (MGVTBL *)&apreq_xs_param_table_magic;
195     SvMAGIC(sv)->mg_flags |= MGf_COPY;
196 
197 #endif
198 
199     sv_magic(sv, rv, PERL_MAGIC_tied, Nullch, 0);
200     SvREFCNT_dec(rv); /* corrects SvREFCNT_inc(rv) implicit in sv_magic */
201 
202     return sv_bless(newRV_noinc(sv), SvSTASH(SvRV(rv)));
203 }
204 
205 
206 
apreq_xs_param_table_keys(void * data,const char * key,const char * val)207 static int apreq_xs_param_table_keys(void *data, const char *key,
208                                      const char *val)
209 {
210 #ifdef USE_ITHREADS
211     struct apreq_xs_do_arg *d = (struct apreq_xs_do_arg *)data;
212     dTHXa(d->perl);
213 #endif
214     dSP;
215     apreq_param_t *p = apreq_value_to_param(val);
216     SV *sv = newSVpvn(key, p->v.nlen);
217 
218 #ifndef USE_ITHREADS
219     (void)data;
220 #endif
221 
222     if (apreq_param_is_tainted(p))
223         SvTAINTED_on(sv);
224 
225     XPUSHs(sv_2mortal(sv));
226     PUTBACK;
227     return 1;
228 }
229 
apreq_xs_param_table_values(void * data,const char * key,const char * val)230 static int apreq_xs_param_table_values(void *data, const char *key,
231                                        const char *val)
232 {
233     struct apreq_xs_do_arg *d = (struct apreq_xs_do_arg *)data;
234     dTHXa(d->perl);
235     dSP;
236     apreq_param_t *p = apreq_value_to_param(val);
237     SV *sv = apreq_xs_param2sv(aTHX_ p, d->pkg, d->parent);
238 
239     XPUSHs(sv_2mortal(sv));
240     PUTBACK;
241     return 1;
242 }
243 
244 
245 
246 #endif /* APREQ_XS_TABLES_H */
247