File Coverage

Encoder.xs
Criterion Covered Total %
statement 103 109 94.5
branch 45 78 57.6
condition n/a
subroutine n/a
pod n/a
total 148 187 79.1


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 944896           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 944896           dSP;
38              
39 944896 100         header_sv = has_hdr ? POPs : NULL;
40 944896           body_sv = POPs;
41 944896           PUTBACK;
42              
43 944896           encoder_ref_sv = TOPs;
44              
45 944896 50         if (!expect_true(
    50          
    50          
    50          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    50          
    50          
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 944896 50         enc= INT2PTR(srl_encoder_t *,SvIV(encoder_sv));
58              
59 944896 100         if (header_sv && !SvOK(header_sv))
    100          
    50          
    50          
60 168           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 944896           ret_sv= srl_dump_data_structure_mortal_sv(aTHX_ enc, body_sv, header_sv, SRL_ENC_SV_COPY_ALWAYS);
67 944893           SPAGAIN;
68 944893           TOPs = ret_sv;
69 944893           }
70              
71             #if USE_CUSTOM_OPS
72              
73             static OP *
74 243721           THX_pp_sereal_encode_with_object(pTHX)
75             {
76 243721           pp1_sereal_encode_with_object(PL_op->op_private);
77 243721           return NORMAL;
78             }
79              
80             static OP *
81 84           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 84           entersubop = ck_entersub_args_proto(entersubop, namegv, ckobj);
90 84           pushop = cUNOPx(entersubop)->op_first;
91 84 50         if (!OpHAS_SIBLING(pushop))
92 84           pushop = cUNOPx(pushop)->op_first;
93 84 50         firstargop = OpSIBLING(pushop);
94              
95 252 50         for (cvop = firstargop; OpHAS_SIBLING(cvop); cvop = OpSIBLING(cvop)) ;
    100          
96              
97 252 100         for (arity = 0, lastargop = pushop, argop = firstargop; argop != cvop;
98 168 50         lastargop = argop, argop = OpSIBLING(argop))
99             {
100 168           arity++;
101             }
102              
103 84 50         if (expect_false(arity < 2 || arity > 3))
    50          
    50          
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 84           op_sibling_splice(NULL, pushop, arity, NULL);
114             /* then throw everything else out */
115 84           op_free(entersubop);
116 84           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 84           newop->op_type = OP_CUSTOM;
128 84           newop->op_private = arity == 3;
129 84           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 84           op_sibling_splice(newop, NULL, 1, firstargop);
136              
137             #endif
138              
139 84           return newop;
140             }
141              
142             #endif /* USE_CUSTOM_OPS */
143              
144             static void
145 701175           THX_xsfunc_sereal_encode_with_object(pTHX_ CV *cv)
146             {
147 701175           dMARK;
148 701175           dSP;
149 701175           SSize_t arity = SP - MARK;
150             PERL_UNUSED_ARG(cv);
151 701175 50         if (arity < 2 || arity > 3)
    50          
152 0           croak("bad Sereal encoder usage");
153 701175           pp1_sereal_encode_with_object(arity == 3);
154 701172           }
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 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_ALIASED_DEDUPE_STRINGS, SRL_ENC_OPT_STR_ALIASED_DEDUPE_STRINGS );
172 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_CANONICAL, SRL_ENC_OPT_STR_CANONICAL );
173 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_CANONICAL_REFS, SRL_ENC_OPT_STR_CANONICAL_REFS );
174 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_COMPRESS, SRL_ENC_OPT_STR_COMPRESS );
175 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_COMPRESS_LEVEL, SRL_ENC_OPT_STR_COMPRESS_LEVEL );
176 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_COMPRESS_THRESHOLD, SRL_ENC_OPT_STR_COMPRESS_THRESHOLD );
177 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_CROAK_ON_BLESS, SRL_ENC_OPT_STR_CROAK_ON_BLESS );
178 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_DEDUPE_STRINGS, SRL_ENC_OPT_STR_DEDUPE_STRINGS );
179 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_FREEZE_CALLBACKS, SRL_ENC_OPT_STR_FREEZE_CALLBACKS );
180 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_MAX_RECURSION_DEPTH, SRL_ENC_OPT_STR_MAX_RECURSION_DEPTH );
181 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_NO_BLESS_OBJECTS, SRL_ENC_OPT_STR_NO_BLESS_OBJECTS );
182 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_NO_SHARED_HASHKEYS, SRL_ENC_OPT_STR_NO_SHARED_HASHKEYS );
183 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_PROTOCOL_VERSION, SRL_ENC_OPT_STR_PROTOCOL_VERSION );
184 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_SNAPPY, SRL_ENC_OPT_STR_SNAPPY );
185 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_SNAPPY_INCR, SRL_ENC_OPT_STR_SNAPPY_INCR );
186 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_SNAPPY_THRESHOLD, SRL_ENC_OPT_STR_SNAPPY_THRESHOLD );
187 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_SORT_KEYS, SRL_ENC_OPT_STR_SORT_KEYS );
188 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_STRINGIFY_UNKNOWN, SRL_ENC_OPT_STR_STRINGIFY_UNKNOWN );
189 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_UNDEF_UNKNOWN, SRL_ENC_OPT_STR_UNDEF_UNKNOWN );
190 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_USE_PROTOCOL_V1, SRL_ENC_OPT_STR_USE_PROTOCOL_V1 );
191 87           SRL_INIT_OPTION( SRL_ENC_OPT_IDX_WARN_UNKNOWN, SRL_ENC_OPT_STR_WARN_UNKNOWN );
192 87           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 87           Newxz(xop, 1, XOP);
198 87           XopENTRY_set(xop, xop_name, "sereal_encode_with_object");
199 87           XopENTRY_set(xop, xop_desc, "sereal_encode_with_object");
200 87           XopENTRY_set(xop, xop_class, OA_UNOP);
201 87           Perl_custom_op_register(aTHX_ THX_pp_sereal_encode_with_object, xop);
202             }
203             #endif /* USE_CUSTOM_OPS */
204             {
205             GV *gv;
206 87           CV *cv = newXSproto_portable("Sereal::Encoder::sereal_encode_with_object",
207             THX_xsfunc_sereal_encode_with_object, __FILE__, "$$;$");
208             #if USE_CUSTOM_OPS
209 87           cv_set_call_checker(cv, THX_ck_entersub_args_sereal_encode_with_object, (SV*)cv);
210             #endif /* USE_CUSTOM_OPS */
211 87           gv = gv_fetchpv("Sereal::Encoder::encode", GV_ADDMULTI, SVt_PVCV);
212 87           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 338           RETVAL = srl_build_encoder_struct(aTHX_ opt, MY_CXT.options);
224 338           RETVAL->flags |= SRL_F_REUSE_ENCODER;
225             OUTPUT: RETVAL
226              
227             void
228             DESTROY(enc)
229             srl_encoder_t *enc;
230             CODE:
231 338           srl_destroy_encoder(aTHX_ enc);
232              
233             U32
234             flags(enc)
235             srl_encoder_t *enc;
236             CODE:
237 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 244246           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 244246           ST(0) = srl_dump_data_structure_mortal_sv(aTHX_ enc, src, NULL, SRL_ENC_SV_REUSE_MAYBE);
254 244245           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 2 50         if (!SvOK(hdr_user_data_src))
    0          
    0          
266 0           hdr_user_data_src = NULL;
267 2           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 2           ST(0) = srl_dump_data_structure_mortal_sv(aTHX_ enc, src, hdr_user_data_src, SRL_ENC_SV_REUSE_MAYBE);
273 2           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 1           UV i, n = 20;
284             char *check[20];
285 1           char fail[5] = "not ";
286 1           char noop[1] = "";
287             CODE:
288 1           tbl = PTABLE_new_size(10);
289 21 100         for (i = 0; i < (UV)n; ++i) {
290 20           PTABLE_store(tbl, INT2PTR(void *,(1000+i)), INT2PTR(void *, (1000+i)));
291 20           check[i] = fail;
292             }
293 21 100         for (i = 0; i < (UV)n; ++i) {
294 20           const UV res = PTR2UV(PTABLE_fetch(tbl, INT2PTR(void *, (1000+i))));
295 20 50         printf("%sok %u - fetch %u\n", (res == (UV)(1000+i)) ? noop : fail, (unsigned int)(1+i), (unsigned int)(i+1));
296             }
297 1           iter = PTABLE_iter_new(tbl);
298 21 100         while ( NULL != (ent = PTABLE_iter_next(iter)) ) {
299 20           const UV res = (PTR2UV(ent->value)) - 1000;
300 20 50         if (res < 20)
301 20           check[res] = noop;
302             else
303 0           abort();
304             }
305 21 100         for (i = 0; i < (UV)n; ++i) {
306 20           printf("%sok %u - iter %u\n", check[i], (unsigned int)(21+i), (unsigned int)(i+1));
307             }
308 1           PTABLE_iter_free(iter);
309 1           PTABLE_free(tbl);