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