File Coverage

Encoder.xs
Criterion Covered Total %
statement 0 109 0.0
branch 0 70 0.0
condition n/a
subroutine n/a
pod n/a
total 0 179 0.0


line stmt bran cond sub pod time code
1             /* Must be defined before including Perl header files or we slow down by 2x! */
2             #define PERL_NO_GET_CONTEXT
3              
4             #include "EXTERN.h"
5             #include "perl.h"
6             #include "XSUB.h"
7              
8             #define NEED_newSV_type_GLOBAL
9             #include "ppport.h"
10              
11             #include "srl_encoder.h"
12             #include "srl_buffer.h"
13              
14             /* Generated code for exposing C constants to Perl */
15             #include "srl_protocol.h"
16              
17             #include "ptable.h"
18              
19             #ifndef GvCV_set
20             # define GvCV_set(gv, cv) (GvCV(gv) = (cv))
21             #endif
22              
23             #if defined(cv_set_call_checker) && defined(XopENTRY_set)
24             # define USE_CUSTOM_OPS 1
25             #else
26             # define USE_CUSTOM_OPS 0
27             #endif
28              
29             #define pp1_sereal_encode_with_object(has_hdr) THX_pp1_sereal_encode_with_object(aTHX_ has_hdr)
30             static void
31 0           THX_pp1_sereal_encode_with_object(pTHX_ U8 has_hdr)
32             {
33             SV *encoder_ref_sv, *encoder_sv, *body_sv, *header_sv;
34             srl_encoder_t *enc;
35             char *stash_name;
36             SV *ret_sv;
37 0           dSP;
38              
39 0 0         header_sv = has_hdr ? POPs : NULL;
40 0           body_sv = POPs;
41 0           PUTBACK;
42              
43 0           encoder_ref_sv = TOPs;
44              
45 0 0         if (!expect_true(
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
46             encoder_ref_sv &&
47             SvROK(encoder_ref_sv) &&
48             (encoder_sv = SvRV(encoder_ref_sv)) &&
49             SvOBJECT(encoder_sv) &&
50             (stash_name= HvNAME(SvSTASH(encoder_sv))) &&
51             !strcmp(stash_name, "Sereal::Encoder")
52             ))
53             {
54 0           croak("handle is not a Sereal::Encoder handle");
55             }
56             /* we should never have an IV smaller than a PTR */
57 0           enc= INT2PTR(srl_encoder_t *,SvIV(encoder_sv));
58              
59 0 0         if (header_sv && !SvOK(header_sv))
    0          
60 0           header_sv = NULL;
61              
62             /* We always copy the string since we might reuse the string buffer. That
63             * means we already have to do a malloc and we might as well use the
64             * opportunity to allocate only as much memory as we really need to hold
65             * the output. */
66 0           ret_sv= srl_dump_data_structure_mortal_sv(aTHX_ enc, body_sv, header_sv, SRL_ENC_SV_COPY_ALWAYS);
67 0           SPAGAIN;
68 0           TOPs = ret_sv;
69 0           }
70              
71             #if USE_CUSTOM_OPS
72              
73             static OP *
74 0           THX_pp_sereal_encode_with_object(pTHX)
75             {
76 0           pp1_sereal_encode_with_object(PL_op->op_private);
77 0           return NORMAL;
78             }
79              
80             static OP *
81 0           THX_ck_entersub_args_sereal_encode_with_object(pTHX_ OP *entersubop, GV *namegv, SV *ckobj)
82             {
83             OP *pushop, *firstargop, *cvop, *lastargop, *argop, *newop;
84             int arity;
85              
86             /* Walk the OP structure under the "entersub" to validate that we
87             * can use the custom OP implementation. */
88              
89 0           entersubop = ck_entersub_args_proto(entersubop, namegv, ckobj);
90 0           pushop = cUNOPx(entersubop)->op_first;
91 0 0         if (!OpHAS_SIBLING(pushop))
92 0           pushop = cUNOPx(pushop)->op_first;
93 0 0         firstargop = OpSIBLING(pushop);
94              
95 0 0         for (cvop = firstargop; OpHAS_SIBLING(cvop); cvop = OpSIBLING(cvop)) ;
    0          
96              
97 0 0         for (arity = 0, lastargop = pushop, argop = firstargop; argop != cvop;
98 0           lastargop = argop, argop = OpSIBLING(argop))
99             {
100 0 0         arity++;
101             }
102              
103 0 0         if (expect_false(arity < 2 || arity > 3))
    0          
    0          
104 0           return entersubop;
105              
106             /* If we get here, we can replace the entersub with a suitable
107             * sereal_encode_with_object custom OP. */
108              
109             #ifdef op_sibling_splice
110             /* op_sibling_splice is new in 5.31 and we have to do things differenly */
111              
112             /* cut out all ops between the pushmark and the RV2CV */
113 0           op_sibling_splice(NULL, pushop, arity, NULL);
114             /* then throw everything else out */
115 0           op_free(entersubop);
116 0           newop = newUNOP(OP_NULL, 0, NULL);
117              
118             #else
119              
120             OpMORESIB_set(pushop, cvop);
121             OpLASTSIB_set(lastargop, op_parent(lastargop));
122             op_free(entersubop);
123             newop = newUNOP(OP_NULL, 0, firstargop);
124              
125             #endif
126              
127 0           newop->op_type = OP_CUSTOM;
128 0           newop->op_private = arity == 3;
129 0           newop->op_ppaddr = THX_pp_sereal_encode_with_object;
130              
131             #ifdef op_sibling_splice
132              
133             /* attach the spliced-out args as children of the custom op, while
134             * deleting the stub op created by newUNOP() */
135 0           op_sibling_splice(newop, NULL, 1, firstargop);
136              
137             #endif
138              
139 0           return newop;
140             }
141              
142             #endif /* USE_CUSTOM_OPS */
143              
144             static void
145 0           THX_xsfunc_sereal_encode_with_object(pTHX_ CV *cv)
146             {
147 0           dMARK;
148 0           dSP;
149 0           SSize_t arity = SP - MARK;
150             PERL_UNUSED_ARG(cv);
151 0 0         if (arity < 2 || arity > 3)
    0          
152 0           croak("bad Sereal encoder usage");
153 0           pp1_sereal_encode_with_object(arity == 3);
154 0           }
155              
156             #define MY_CXT_KEY "Sereal::Encoder::_stash" XS_VERSION
157              
158             typedef struct {
159             sv_with_hash options[SRL_ENC_OPT_COUNT];
160             } my_cxt_t;
161              
162             START_MY_CXT
163              
164             MODULE = Sereal::Encoder PACKAGE = Sereal::Encoder
165             PROTOTYPES: DISABLE
166              
167             BOOT:
168             {
169             {
170             MY_CXT_INIT;
171 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_ALIASED_DEDUPE_STRINGS, SRL_ENC_OPT_STR_ALIASED_DEDUPE_STRINGS );
172 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_CANONICAL, SRL_ENC_OPT_STR_CANONICAL );
173 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_CANONICAL_REFS, SRL_ENC_OPT_STR_CANONICAL_REFS );
174 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_COMPRESS, SRL_ENC_OPT_STR_COMPRESS );
175 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_COMPRESS_LEVEL, SRL_ENC_OPT_STR_COMPRESS_LEVEL );
176 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_COMPRESS_THRESHOLD, SRL_ENC_OPT_STR_COMPRESS_THRESHOLD );
177 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_CROAK_ON_BLESS, SRL_ENC_OPT_STR_CROAK_ON_BLESS );
178 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_DEDUPE_STRINGS, SRL_ENC_OPT_STR_DEDUPE_STRINGS );
179 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_FREEZE_CALLBACKS, SRL_ENC_OPT_STR_FREEZE_CALLBACKS );
180 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_MAX_RECURSION_DEPTH, SRL_ENC_OPT_STR_MAX_RECURSION_DEPTH );
181 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_NO_BLESS_OBJECTS, SRL_ENC_OPT_STR_NO_BLESS_OBJECTS );
182 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_NO_SHARED_HASHKEYS, SRL_ENC_OPT_STR_NO_SHARED_HASHKEYS );
183 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_PROTOCOL_VERSION, SRL_ENC_OPT_STR_PROTOCOL_VERSION );
184 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_SNAPPY, SRL_ENC_OPT_STR_SNAPPY );
185 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_SNAPPY_INCR, SRL_ENC_OPT_STR_SNAPPY_INCR );
186 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_SNAPPY_THRESHOLD, SRL_ENC_OPT_STR_SNAPPY_THRESHOLD );
187 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_SORT_KEYS, SRL_ENC_OPT_STR_SORT_KEYS );
188 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_STRINGIFY_UNKNOWN, SRL_ENC_OPT_STR_STRINGIFY_UNKNOWN );
189 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_UNDEF_UNKNOWN, SRL_ENC_OPT_STR_UNDEF_UNKNOWN );
190 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_USE_PROTOCOL_V1, SRL_ENC_OPT_STR_USE_PROTOCOL_V1 );
191 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_WARN_UNKNOWN, SRL_ENC_OPT_STR_WARN_UNKNOWN );
192 0           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_USE_STANDARD_DOUBLE, SRL_ENC_OPT_STR_USE_STANDARD_DOUBLE );
193             }
194             #if USE_CUSTOM_OPS
195             {
196             XOP *xop;
197 0           Newxz(xop, 1, XOP);
198 0           XopENTRY_set(xop, xop_name, "sereal_encode_with_object");
199 0           XopENTRY_set(xop, xop_desc, "sereal_encode_with_object");
200 0           XopENTRY_set(xop, xop_class, OA_UNOP);
201 0           Perl_custom_op_register(aTHX_ THX_pp_sereal_encode_with_object, xop);
202             }
203             #endif /* USE_CUSTOM_OPS */
204             {
205             GV *gv;
206 0           CV *cv = newXSproto_portable("Sereal::Encoder::sereal_encode_with_object",
207             THX_xsfunc_sereal_encode_with_object, __FILE__, "$$;$");
208             #if USE_CUSTOM_OPS
209 0           cv_set_call_checker(cv, THX_ck_entersub_args_sereal_encode_with_object, (SV*)cv);
210             #endif /* USE_CUSTOM_OPS */
211 0           gv = gv_fetchpv("Sereal::Encoder::encode", GV_ADDMULTI, SVt_PVCV);
212 0           GvCV_set(gv, cv);
213             }
214             }
215              
216             srl_encoder_t *
217             new(CLASS, opt = NULL)
218             char *CLASS;
219             HV *opt;
220             PREINIT:
221             dMY_CXT;
222             CODE:
223 0           RETVAL = srl_build_encoder_struct(aTHX_ opt, MY_CXT.options);
224 0           RETVAL->flags |= SRL_F_REUSE_ENCODER;
225             OUTPUT: RETVAL
226              
227             void
228             DESTROY(enc)
229             srl_encoder_t *enc;
230             CODE:
231 0           srl_destroy_encoder(aTHX_ enc);
232              
233             U32
234             flags(enc)
235             srl_encoder_t *enc;
236             CODE:
237 0 0         RETVAL = enc->flags;
238             OUTPUT: RETVAL
239              
240             void
241             encode_sereal(src, opt = NULL)
242             SV *src;
243             HV *opt;
244             PREINIT:
245             srl_encoder_t *enc;
246             dMY_CXT;
247             PPCODE:
248 0           enc = srl_build_encoder_struct(aTHX_ opt, MY_CXT.options);
249             assert(enc != NULL);
250             /* Avoid copy by stealing string buffer if it is not too large.
251             * This makes sense in the functional interface since the string
252             * buffer isn't ever going to be reused. */
253 0           ST(0) = srl_dump_data_structure_mortal_sv(aTHX_ enc, src, NULL, SRL_ENC_SV_REUSE_MAYBE);
254 0           XSRETURN(1);
255              
256             void
257             encode_sereal_with_header_data(src, hdr_user_data_src, opt = NULL)
258             SV *src;
259             SV *hdr_user_data_src;
260             HV *opt;
261             PREINIT:
262             srl_encoder_t *enc;
263             dMY_CXT;
264             PPCODE:
265 0 0         if (!SvOK(hdr_user_data_src))
266 0           hdr_user_data_src = NULL;
267 0           enc = srl_build_encoder_struct(aTHX_ opt, MY_CXT.options);
268             assert(enc != NULL);
269             /* Avoid copy by stealing string buffer if it is not too large.
270             * This makes sense in the functional interface since the string
271             * buffer isn't ever going to be reused. */
272 0           ST(0) = srl_dump_data_structure_mortal_sv(aTHX_ enc, src, hdr_user_data_src, SRL_ENC_SV_REUSE_MAYBE);
273 0           XSRETURN(1);
274              
275             MODULE = Sereal::Encoder PACKAGE = Sereal::Encoder::_ptabletest
276              
277             void
278             test()
279             PREINIT:
280             PTABLE_t *tbl;
281             PTABLE_ITER_t *iter;
282             PTABLE_ENTRY_t *ent;
283 0           UV i, n = 20;
284             char *check[20];
285 0           char fail[5] = "not ";
286 0           char noop[1] = "";
287             CODE:
288 0           tbl = PTABLE_new_size(10);
289 0 0         for (i = 0; i < (UV)n; ++i) {
290 0           PTABLE_store(tbl, INT2PTR(void *,(1000+i)), INT2PTR(void *, (1000+i)));
291 0           check[i] = fail;
292             }
293 0 0         for (i = 0; i < (UV)n; ++i) {
294 0           const UV res = PTR2UV(PTABLE_fetch(tbl, INT2PTR(void *, (1000+i))));
295 0 0         printf("%sok %u - fetch %u\n", (res == (UV)(1000+i)) ? noop : fail, (unsigned int)(1+i), (unsigned int)(i+1));
296             }
297 0           iter = PTABLE_iter_new(tbl);
298 0 0         while ( NULL != (ent = PTABLE_iter_next(iter)) ) {
299 0           const UV res = (PTR2UV(ent->value)) - 1000;
300 0 0         if (res < 20)
301 0           check[res] = noop;
302             else
303 0           abort();
304             }
305 0 0         for (i = 0; i < (UV)n; ++i) {
306 0           printf("%sok %u - iter %u\n", check[i], (unsigned int)(21+i), (unsigned int)(i+1));
307             }
308 0           PTABLE_iter_free(iter);
309 0           PTABLE_free(tbl);