/* Must be defined before including Perl header files or we slow down by 2x! */
#define PERL_NO_GET_CONTEXT
#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#define NEED_newSV_type
#define NEED_newSVpvn_flags
#include "ppport.h"
#ifdef __cplusplus
}
#endif
#ifndef PERL_VERSION
# include <patchlevel.h>
# if !(defined(PERL_VERSION) || (PERL_SUBVERSION > 0 && defined(PATCHLEVEL)))
# include <could_not_find_Perl_patchlevel.h>
# endif
# define PERL_REVISION 5
# define PERL_VERSION PATCHLEVEL
# define PERL_SUBVERSION PERL_SUBVERSION
#endif
#if PERL_VERSION < 8
# define PERL_MAGIC_qr 'r' /* precompiled qr// regex */
# define BFD_Svs_SMG_OR_RMG SVs_RMG
#elif ((PERL_VERSION==8) && (PERL_SUBVERSION >= 1) || (PERL_VERSION>8))
# define BFD_Svs_SMG_OR_RMG SVs_SMG
# define MY_PLACEHOLDER PL_sv_placeholder
#else
# define BFD_Svs_SMG_OR_RMG SVs_RMG
# define MY_PLACEHOLDER PL_sv_undef
#endif
#if (((PERL_VERSION == 9) && (PERL_SUBVERSION >= 4)) || (PERL_VERSION > 9))
# define NEW_REGEX_ENGINE 1
#endif
#if (((PERL_VERSION == 8) && (PERL_SUBVERSION >= 1)) || (PERL_VERSION > 8))
#define MY_CAN_FIND_PLACEHOLDERS
#define HAS_SV2OBJ
#endif
#if (PERL_VERSION < 10)
# define FIXUP_RITER 1
#endif
#if (PERL_VERSION >= 10)
# define FAST_IV 1
#endif
#define DEFAULT_MAX_RECUR_DEPTH 10000
#if !defined(HAVE_CSNAPPY)
# include "snappy/csnappy_decompress.c"
#endif
#include "srl_decoder.h"
#include "srl_common.h"
#include "ptable.h"
#include "srl_reader.h"
#include "srl_reader_error.h"
#include "srl_reader_varint.h"
#include "srl_reader_misc.h"
#include "srl_reader_decompress.h"
#include "srl_protocol.h"
#include "srl_taginfo.h"
/* 5.8.8 and earlier have a nasty bug in their handling of overloading:
* The overload-flag is set on the referer of the blessed object instead of
* the referent. That means that our late-bless logic breaks for
* multiply-occurring objects.
* So for 5.8.8 and earlier, the easiest workaround is to bless as we go
* instead of blessing at the end of a decode run. Additionally, on repeatedly
* encountered objects (REFP), we have to check the stash of the referent for
* overloadedness and set the OVERLOAD flag (AMAGIC_on) on the NEW referer.
*
* Details on the perl bug in perl589delta.pod,
* see "Reblessing overloaded objects now works".
*
* This is potentially a security problem (destructors!), but we really need
* this to work on 5.8.5 for now, so let's make it work.
* Another way of making it work might be to keep track of all occurrences
* of objects and fix them up afterwards. That seems even more intrusive.
* Please prove us wrong, though, since it's semantically a better fix.
*
* --Eric and Steffen
*/
#if ((PERL_VERSION == 8) && (PERL_SUBVERSION >= 9) || (PERL_VERSION > 8))
# define USE_588_WORKAROUND 0
#else
# define USE_588_WORKAROUND 1
#endif
/* predeclare all our subs so we have one definitive authority for their signatures */
SRL_STATIC_INLINE SV *srl_fetch_item(pTHX_ srl_decoder_t *dec, UV item, const char * const tag_name);
/* these three are "Public" */
srl_decoder_t *srl_build_decoder_struct(pTHX_ HV *opt, sv_with_hash *options); /* constructor - called from ->new() */
void srl_destroy_decoder(pTHX_ srl_decoder_t *dec); /* destructor - called from ->DESTROY() */
void srl_decoder_destructor_hook(pTHX_ void *p); /* destructor hook - called automagically */
/* the top level components of the decode process - called by srl_decode_into() */
/* srl_begin_decoding: set up the decoder to handle a given var */
SRL_STATIC_INLINE srl_decoder_t *srl_begin_decoding(pTHX_ srl_decoder_t *dec, SV *src, UV start_offset);
SRL_STATIC_INLINE void srl_read_header(pTHX_ srl_decoder_t *dec, SV *header_user_data); /* read/validate header */
SRL_STATIC_INLINE void srl_read_single_value(pTHX_ srl_decoder_t *dec, SV* into, SV** container); /* main recursive dump routine */
SRL_STATIC_INLINE void srl_finalize_structure(pTHX_ srl_decoder_t *dec); /* optional finalize structure logic */
SRL_STATIC_INLINE void srl_clear_decoder(pTHX_ srl_decoder_t *dec); /* clean up decoder after a dump */
/* the internal routines to handle each kind of object we have to deserialize */
SRL_STATIC_INLINE void srl_read_copy(pTHX_ srl_decoder_t *dec, SV* into);
SRL_STATIC_INLINE void srl_read_hash(pTHX_ srl_decoder_t *dec, SV* into, U8 tag);
SRL_STATIC_INLINE void srl_read_array(pTHX_ srl_decoder_t *dec, SV* into, U8 tag);
SRL_STATIC_INLINE void srl_read_regexp(pTHX_ srl_decoder_t *dec, SV* into);
SRL_STATIC_INLINE void srl_read_refp(pTHX_ srl_decoder_t *dec, SV* into);
SRL_STATIC_INLINE void srl_read_refn(pTHX_ srl_decoder_t *dec, SV* into);
SRL_STATIC_INLINE void srl_read_weaken(pTHX_ srl_decoder_t *dec, SV* into);
SRL_STATIC_INLINE void srl_read_long_double(pTHX_ srl_decoder_t *dec, SV* into);
SRL_STATIC_INLINE void srl_read_double(pTHX_ srl_decoder_t *dec, SV* into);
SRL_STATIC_INLINE void srl_read_float(pTHX_ srl_decoder_t *dec, SV* into);
SRL_STATIC_INLINE void srl_read_string(pTHX_ srl_decoder_t *dec, int is_utf8, SV* into);
SRL_STATIC_INLINE void srl_read_varint_into(pTHX_ srl_decoder_t *dec, SV* into, SV** container, const U8 *track_it);
SRL_STATIC_INLINE void srl_read_zigzag_into(pTHX_ srl_decoder_t *dec, SV* into, SV** container, const U8 *track_it);
SRL_STATIC_INLINE void srl_read_reserved(pTHX_ srl_decoder_t *dec, U8 tag, SV* into);
SRL_STATIC_INLINE void srl_read_object(pTHX_ srl_decoder_t *dec, SV* into, U8 obj_tag, int read_class_name_only);
SRL_STATIC_INLINE void srl_read_objectv(pTHX_ srl_decoder_t *dec, SV* into, U8 obj_tag);
SRL_STATIC_INLINE void srl_track_sv(pTHX_ srl_decoder_t *dec, const U8 *track_pos, SV *sv);
SRL_STATIC_INLINE void srl_read_frozen_object(pTHX_ srl_decoder_t *dec, HV *class_stash, SV *into);
SRL_STATIC_INLINE SV * srl_follow_refp_alias_reference(pTHX_ srl_decoder_t *dec, UV offset);
SRL_STATIC_INLINE AV * srl_follow_objectv_reference(pTHX_ srl_decoder_t *dec, UV offset);
/* FIXME unimplemented!!! */
SRL_STATIC_INLINE SV *srl_read_extend(pTHX_ srl_decoder_t *dec, SV* into);
#define DEPTH_INCREMENT(dec) STMT_START { \
if (expect_false(++dec->recursion_depth > dec->max_recursion_depth)) { \
SRL_RDR_ERRORf1(dec->pbuf, "Reached recursion limit (%"UVuf") during deserialization", \
(UV)dec->max_recursion_depth); \
} \
} STMT_END
#define DEPTH_DECREMENT(dec) dec->recursion_depth--
#define IS_SRL_HDR_ARRAYREF(tag) (((tag) & SRL_HDR_ARRAYREF) == SRL_HDR_ARRAYREF)
#define IS_SRL_HDR_HASHREF(tag) (((tag) & SRL_HDR_HASHREF) == SRL_HDR_HASHREF)
#define IS_SRL_HDR_SHORT_BINARY(tag) (((tag) & SRL_HDR_SHORT_BINARY_LOW) == SRL_HDR_SHORT_BINARY_LOW)
#define SRL_HDR_SHORT_BINARY_LEN_FROM_TAG(tag) ((tag) & SRL_MASK_SHORT_BINARY_LEN)
#define SRL_ASSERT_REF_PTR_TABLES(dec) STMT_START { \
if (expect_false( !(dec)->ref_stashes )) { \
(dec)->ref_stashes = PTABLE_new(); \
(dec)->ref_bless_av = PTABLE_new(); \
} \
} STMT_END
#define SRL_sv_set_rv_to(into,referent) \
STMT_START { \
SRL_prepare_SV_for_RV(into); \
SvTEMP_off(referent); \
SvRV_set(into, referent); \
SvROK_on(into); \
} STMT_END
STATIC void
srl_ptable_debug_callback(PTABLE_ENTRY_t *e)
{
dTHX;
printf("KEY=%"UVuf"\nVALUE:\n", (UV)e->key);
sv_dump((SV *)e->value);
printf("\n");
}
SRL_STATIC_INLINE void
srl_ptable_debug_dump(pTHX_ PTABLE_t *tbl)
{
PTABLE_debug_dump(tbl, srl_ptable_debug_callback);
}
#define my_hv_fetchs(he,val,opt,idx) STMT_START { \
he = hv_fetch_ent(opt, options[idx].sv, 0, options[idx].hash); \
if (he) \
val= HeVAL(he); \
else \
val= NULL; \
} STMT_END
/* Multiple places in this file that want to use srl_read_varint_uv_length
* but don't have a buffer struct handy. */
SRL_STATIC_INLINE UV
S_read_varint_uv_length_char_ptr(pTHX_ const unsigned char **from, const unsigned char *end, const char * const errstr)
{
UV rv;
srl_reader_buffer_t buf;
buf.pos = *from;
buf.end = end;
buf.start = NULL; /* meh */
rv = srl_read_varint_uv_length(aTHX_ &buf, errstr);
*from = buf.pos;
return rv;
}
/* PUBLIC ROUTINES */
/* Builds the C-level configuration and state struct.
* Automatically freed at scope boundary. */
srl_decoder_t *
srl_build_decoder_struct(pTHX_ HV *opt, sv_with_hash *options)
{
srl_decoder_t *dec;
SV *val;
HE *he;
Newxz(dec, 1, srl_decoder_t);
dec->ref_seenhash = PTABLE_new();
dec->max_recursion_depth = DEFAULT_MAX_RECUR_DEPTH;
dec->max_num_hash_entries = 0; /* 0 == any number */
SRL_RDR_CLEAR(&dec->buf);
dec->pbuf = &dec->buf;
/* load options */
if (opt != NULL) {
my_hv_fetchs(he,val,opt, SRL_DEC_OPT_IDX_REFUSE_SNAPPY);
if ( val && SvTRUE(val) )
SRL_DEC_SET_OPTION(dec, SRL_F_DECODER_REFUSE_SNAPPY);
my_hv_fetchs(he,val,opt, SRL_DEC_OPT_IDX_REFUSE_ZLIB);
if ( val && SvTRUE(val) )
SRL_DEC_SET_OPTION(dec, SRL_F_DECODER_REFUSE_ZLIB);
my_hv_fetchs(he,val,opt, SRL_DEC_OPT_IDX_REFUSE_ZSTD);
if ( val && SvTRUE(val) )
SRL_DEC_SET_OPTION(dec, SRL_F_DECODER_REFUSE_ZSTD);
my_hv_fetchs(he,val,opt, SRL_DEC_OPT_IDX_REFUSE_OBJECTS);
if ( val && SvTRUE(val) )
SRL_DEC_SET_OPTION(dec, SRL_F_DECODER_REFUSE_OBJECTS);
my_hv_fetchs(he,val,opt, SRL_DEC_OPT_IDX_NO_BLESS_OBJECTS);
if ( val && SvTRUE(val) )
SRL_DEC_SET_OPTION(dec, SRL_F_DECODER_NO_BLESS_OBJECTS);
my_hv_fetchs(he,val,opt, SRL_DEC_OPT_IDX_VALIDATE_UTF8);
if ( val && SvTRUE(val) )
SRL_DEC_SET_OPTION(dec, SRL_F_DECODER_VALIDATE_UTF8);
my_hv_fetchs(he,val,opt, SRL_DEC_OPT_IDX_MAX_RECURSION_DEPTH);
if ( val && SvTRUE(val) )
dec->max_recursion_depth = SvUV(val);
my_hv_fetchs(he,val,opt, SRL_DEC_OPT_IDX_MAX_NUM_HASH_ENTRIES);
if ( val && SvTRUE(val) )
dec->max_num_hash_entries = SvUV(val);
my_hv_fetchs(he,val,opt, SRL_DEC_OPT_IDX_DESTRUCTIVE_INCREMENTAL);
if ( val && SvTRUE(val) )
SRL_DEC_SET_OPTION(dec,SRL_F_DECODER_DESTRUCTIVE_INCREMENTAL);
/* see if they want us to alias varints, value is an unsigned integer.
* setting it to a true value smaller than 16 is the same as
* using the "alias_smallint" option. Setting it to a true value larger
* than 15 enables aliasing of smallints, and implies "alias_smallint" as
* well. */
my_hv_fetchs(he,val,opt, SRL_DEC_OPT_IDX_ALIAS_VARINT_UNDER);
if ( val && SvTRUE(val)) {
/* if they use this then they automatically imply doing it for
* smallint as well */
SRL_DEC_SET_OPTION(dec,SRL_F_DECODER_ALIAS_SMALLINT);
SRL_DEC_SET_OPTION(dec,SRL_F_DECODER_ALIAS_VARINT);
if (SvUV(val) < 16) {
/* too small, just enable for SMALLINT (POS/NEG)*/
dec->alias_varint_under= 16;
} else {
/* larger than POS/NEG range, also alias some VARINTs */
/* anything smaller than this number will be aliased */
dec->alias_varint_under= SvUV(val);
}
/* create the alias cache */
dec->alias_cache= newAV();
}
/* they can enable aliasing of SMALLINT's alone */
if ( !SRL_DEC_HAVE_OPTION(dec,SRL_F_DECODER_ALIAS_SMALLINT) ) {
my_hv_fetchs(he,val,opt, SRL_DEC_OPT_IDX_ALIAS_SMALLINT);
if (val && SvTRUE(val))
{
/* set the flag */
SRL_DEC_SET_OPTION(dec,SRL_F_DECODER_ALIAS_SMALLINT);
/* create the alias cache */
dec->alias_cache= newAV();
dec->alias_varint_under=16;
}
}
if (dec->alias_varint_under) {
/* extend it to the right size 16 for NEG,
* dec->alias_varint_under is at least 15, and 1 more for zero,
* so we allocate enough for POS/NEG as well as for the additional varints*/
av_extend(dec->alias_cache, 16 + dec->alias_varint_under);
AvFILLp(dec->alias_cache)= 16 + dec->alias_varint_under - 1; /* remove 1 as this is $#ary */
}
/* check if they want us to use &PL_sv_undef for SRL_HEADER_UNDEF
* even if this might break referential integrity. */
my_hv_fetchs(he,val,opt, SRL_DEC_OPT_IDX_USE_UNDEF);
if ( val && SvTRUE(val))
SRL_DEC_SET_OPTION(dec,SRL_F_DECODER_USE_UNDEF);
/* check if they want us to set all SVs readonly. */
my_hv_fetchs(he,val,opt, SRL_DEC_OPT_IDX_SET_READONLY);
if ( val && SvTRUE(val))
SRL_DEC_SET_OPTION(dec, SRL_F_DECODER_SET_READONLY);
/* check if they want us to set normal scalars readonly. */
my_hv_fetchs(he,val,opt, SRL_DEC_OPT_IDX_SET_READONLY_SCALARS);
if ( val && SvTRUE(val))
SRL_DEC_SET_OPTION(dec, SRL_F_DECODER_SET_READONLY_SCALARS);
}
dec->flags_readonly= SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_SET_READONLY ) ? 1 :
SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_SET_READONLY_SCALARS) ? 2 :
0;
return dec;
}
/* Clone a decoder whilst resetting ephemeral state on the clone. */
SRL_STATIC_INLINE srl_decoder_t *
srl_build_decoder_struct_alike(pTHX_ srl_decoder_t *proto)
{
srl_decoder_t *dec;
Newxz(dec, 1, srl_decoder_t);
dec->ref_seenhash = PTABLE_new();
dec->max_recursion_depth = proto->max_recursion_depth;
dec->max_num_hash_entries = proto->max_num_hash_entries;
if (proto->alias_cache) {
dec->alias_cache = proto->alias_cache;
SvREFCNT_inc(dec->alias_cache);
}
SRL_RDR_CLEAR(&dec->buf);
dec->pbuf = &dec->buf;
dec->flags = proto->flags;
SRL_DEC_RESET_VOLATILE_FLAGS(dec);
return dec;
}
/* Explicit destructor */
void
srl_destroy_decoder(pTHX_ srl_decoder_t *dec)
{
PTABLE_free(dec->ref_seenhash);
if (dec->ref_stashes) {
PTABLE_free(dec->ref_stashes);
PTABLE_free(dec->ref_bless_av);
}
if (dec->weakref_av) {
SvREFCNT_dec(dec->weakref_av);
dec->weakref_av = NULL;
}
if (dec->ref_thawhash)
PTABLE_free(dec->ref_thawhash);
if (dec->alias_cache)
SvREFCNT_dec(dec->alias_cache);
Safefree(dec);
}
/* This is fired when we exit the Perl pseudo-block.
* It frees our decoder and all. Put decoder-level cleanup
* logic here so that we can simply use croak/longjmp for
* exception handling. Makes life vastly easier!
*/
void
srl_decoder_destructor_hook(pTHX_ void *p)
{
srl_decoder_t *dec = (srl_decoder_t *)p;
/* Only free decoder if not for reuse */
if (!SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_REUSE)) {
srl_destroy_decoder(aTHX_ dec);
}
else {
/* Clear instead - decoder reused */
srl_clear_decoder(aTHX_ dec);
}
}
/* Logic shared by the various decoder entry points. */
SRL_STATIC_INLINE void
srl_decode_into_internal(pTHX_ srl_decoder_t *origdec, SV *src, SV *header_into, SV *body_into, UV start_offset)
{
srl_decoder_t *dec;
assert(origdec != NULL);
dec = srl_begin_decoding(aTHX_ origdec, src, start_offset);
srl_read_header(aTHX_ dec, header_into);
if (expect_false( SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_DECOMPRESS_SNAPPY) )) {
dec->bytes_consumed = srl_decompress_body_snappy(aTHX_ dec->pbuf, dec->encoding_flags, NULL);
origdec->bytes_consumed = dec->bytes_consumed;
} else if (expect_false( SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_DECOMPRESS_ZLIB) )) {
dec->bytes_consumed = srl_decompress_body_zlib(aTHX_ dec->pbuf, NULL);
origdec->bytes_consumed = dec->bytes_consumed;
} else if (expect_false( SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_DECOMPRESS_ZSTD) )) {
dec->bytes_consumed = srl_decompress_body_zstd(aTHX_ dec->pbuf, NULL);
origdec->bytes_consumed = dec->bytes_consumed;
}
/* this function *MUST* be called right after srl_decompress* functions */
SRL_RDR_UPDATE_BODY_POS(dec->pbuf, dec->proto_version);
/* The actual document body deserialization: */
srl_read_single_value(aTHX_ dec, body_into, NULL);
if (expect_false(SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_NEEDS_FINALIZE))) {
srl_finalize_structure(aTHX_ dec);
}
/* If we aren't reading from a decompressed buffer we have to remember the number
* of bytes used for the user to query. */
if (dec->bytes_consumed == 0) {
dec->bytes_consumed = dec->buf.pos - dec->buf.start;
origdec->bytes_consumed = dec->bytes_consumed;
}
if (SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_DESTRUCTIVE_INCREMENTAL)) {
STRLEN len;
char *pv= SvPV(src,len);
/* check the length here? do something different if the string is now exhausted? */
sv_chop(src, pv + dec->bytes_consumed);
}
srl_clear_decoder(aTHX_ dec);
}
/* This is the main routine to deserialize just the header of a document. */
SV *
srl_decode_header_into(pTHX_ srl_decoder_t *origdec, SV *src, SV* header_into, UV start_offset)
{
srl_decoder_t *dec;
assert(origdec != NULL);
dec = srl_begin_decoding(aTHX_ origdec, src, start_offset);
if (header_into == NULL)
header_into = sv_newmortal();
srl_read_header(aTHX_ dec, header_into);
return header_into;
}
/* this SHOULD be newSV_type(SVt_NULL) but newSV(0) is faster :-( */
#if 1
#define FRESH_SV() newSV(0)
#else
#define FRESH_SV() newSV_type(SVt_NULL);
#endif
/* This is the main routine to deserialize a Sereal document
* w/o data in header. */
SV *
srl_decode_into(pTHX_ srl_decoder_t *dec, SV *src, SV* body_into, UV start_offset)
{
if (expect_true(!body_into))
body_into= sv_2mortal(FRESH_SV());
srl_decode_into_internal(aTHX_ dec, src, NULL, body_into, start_offset);
return body_into;
}
/* This is the main routine to deserialize Sereal document body
* and header all at once. */
void
srl_decode_all_into(pTHX_ srl_decoder_t *dec, SV *src, SV *header_into, SV *body_into, UV start_offset)
{
assert(header_into != NULL);
assert(body_into != NULL);
(void)srl_decode_into_internal(aTHX_ dec, src, header_into, body_into, start_offset);
}
/* TOP LEVEL PRIVATE ROUTINES */
SRL_STATIC_INLINE void
srl_clear_decoder(pTHX_ srl_decoder_t *dec)
{
if (dec->buf.start == dec->buf.end)
return;
srl_clear_decoder_body_state(aTHX_ dec);
SRL_DEC_RESET_VOLATILE_FLAGS(dec);
dec->buf.body_pos = dec->buf.start = dec->buf.end = dec->buf.pos = dec->save_pos = NULL;
}
void
srl_clear_decoder_body_state(pTHX_ srl_decoder_t *dec)
{
SRL_DEC_UNSET_OPTION(dec, SRL_F_DECODER_NEEDS_FINALIZE);
if (dec->weakref_av)
av_clear(dec->weakref_av);
PTABLE_clear(dec->ref_seenhash);
if (dec->ref_stashes) {
PTABLE_clear(dec->ref_stashes);
PTABLE_clear(dec->ref_bless_av);
}
dec->recursion_depth = 0;
}
SRL_STATIC_INLINE srl_decoder_t *
srl_begin_decoding(pTHX_ srl_decoder_t *dec, SV *src, UV start_offset)
{
STRLEN len;
unsigned char *tmp;
/* Check whether decoder is in use and create a new one on the
* fly if necessary. Should only happen in edge cases such as
* a THAW hook calling back into the same decoder. */
if (SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_DIRTY)) {
srl_decoder_t * const proto = dec;
dec = srl_build_decoder_struct_alike(aTHX_ proto);
SRL_DEC_UNSET_OPTION(dec, SRL_F_DECODER_REUSE);
}
/* Needs to be before setting DIRTY because DIRTY is volatile. */
SRL_DEC_RESET_VOLATILE_FLAGS(dec);
/* Set to being in use. */;
SRL_DEC_SET_OPTION(dec, SRL_F_DECODER_DIRTY);
/* Register our structure for destruction on scope exit */
SAVEDESTRUCTOR_X(&srl_decoder_destructor_hook, (void *)dec);
if (SvUTF8(src)) {
/* If we are being asked to decode a utf8-on string then we
* make a mortal copy, and then try to downgrade the copy.
* The downgrade will croak if it cannot successfully downgrade
* the buffer. If it is sucessful then decode the downgraded
* copy.
* Note, we do not make the copy when we are in destructive parsing mode
* as we then are expected to modify the original string.
*/
if ( ! SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_DESTRUCTIVE_INCREMENTAL) ) {
src= sv_mortalcopy(src);
}
sv_utf8_downgrade(src, 0);
}
tmp = (unsigned char*)SvPV(src, len);
if (expect_false( start_offset > len )) {
SRL_RDR_ERROR(dec->pbuf, "Start offset is beyond input string length");
}
dec->buf.start= dec->buf.pos= tmp + start_offset;
dec->buf.end= dec->buf.start + len - start_offset;
SRL_RDR_SET_BODY_POS(dec->pbuf, dec->buf.start);
dec->bytes_consumed = 0;
return dec;
}
IV
srl_validate_header_version_pv_len(pTHX_ char *strdata, STRLEN len)
{
return srl_validate_header_version(aTHX_ (srl_reader_char_ptr) strdata, len);
}
SRL_STATIC_INLINE void
srl_read_header(pTHX_ srl_decoder_t *dec, SV *header_user_data)
{
UV header_len;
IV proto_version_and_encoding_flags_int= srl_validate_header_version(aTHX_ dec->buf.pos, SRL_RDR_SPACE_LEFT(dec->pbuf));
if ( expect_false(proto_version_and_encoding_flags_int < 1) ) {
if (proto_version_and_encoding_flags_int == 0)
SRL_RDR_ERROR(dec->pbuf, "Bad Sereal header: It seems your document was accidentally UTF-8 encoded");
else
SRL_RDR_ERROR(dec->pbuf, "Bad Sereal header: Not a valid Sereal document.");
}
else {
dec->buf.pos += 5;
dec->proto_version = (U8)(proto_version_and_encoding_flags_int & SRL_PROTOCOL_VERSION_MASK);
dec->encoding_flags = (U8)(proto_version_and_encoding_flags_int & SRL_PROTOCOL_ENCODING_MASK);
if (expect_false( dec->proto_version == 1 ))
SRL_DEC_SET_OPTION(dec, SRL_F_DECODER_PROTOCOL_V1); /* compat mode */
else if (expect_false( dec->proto_version > SRL_PROTOCOL_VERSION || dec->proto_version < 1 ))
SRL_RDR_ERRORf1(dec->pbuf, "Unsupported Sereal protocol version %u", dec->proto_version);
if (dec->encoding_flags == SRL_PROTOCOL_ENCODING_RAW) {
/* no op */
}
else
if ( dec->encoding_flags == SRL_PROTOCOL_ENCODING_SNAPPY
|| dec->encoding_flags == SRL_PROTOCOL_ENCODING_SNAPPY_INCREMENTAL)
{
if (expect_false( SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_REFUSE_SNAPPY) )) {
SRL_RDR_ERROR(dec->pbuf, "Sereal document is compressed with Snappy, "
"but this decoder is configured to refuse Snappy-compressed input.");
}
dec->flags |= SRL_F_DECODER_DECOMPRESS_SNAPPY;
}
else
if (dec->encoding_flags == SRL_PROTOCOL_ENCODING_ZLIB)
{
if (expect_false( SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_REFUSE_ZLIB) )) {
SRL_RDR_ERROR(dec->pbuf, "Sereal document is compressed with ZLIB, "
"but this decoder is configured to refuse ZLIB-compressed input.");
}
dec->flags |= SRL_F_DECODER_DECOMPRESS_ZLIB;
}
else
if (dec->encoding_flags == SRL_PROTOCOL_ENCODING_ZSTD)
{
if (expect_false( SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_REFUSE_ZSTD) )) {
SRL_RDR_ERROR(dec->pbuf, "Sereal document is compressed with ZSTD, "
"but this decoder is configured to refuse ZSTD-compressed input.");
}
dec->flags |= SRL_F_DECODER_DECOMPRESS_ZSTD;
}
else
{
SRL_RDR_ERRORf1(dec->pbuf, "Sereal document encoded in an unknown format '%d'",
dec->encoding_flags >> SRL_PROTOCOL_VERSION_BITS);
}
/* Must do this via a temporary as it modifes dec->buf.pos itself */
header_len= srl_read_varint_uv_length(aTHX_ dec->pbuf, " while reading header");
if (dec->proto_version > 1 && header_len) {
/* We have a protocol V2+ extensible header:
* - 8bit bitfield
* - if lowest bit set, we have custom-header-user-data after the bitfield
* => Only read header user data if an SV* was passed in to fill. */
U8 bitfield;
SRL_RDR_ASSERT_SPACE(dec->pbuf, 1, " while reading header flags");
bitfield = *(dec->buf.pos++);
if (bitfield & SRL_PROTOCOL_HDR_USER_DATA && header_user_data != NULL) {
/* Do an actual document body deserialization for the user data: */
SRL_RDR_UPDATE_BODY_POS(dec->pbuf, dec->proto_version);
srl_read_single_value(aTHX_ dec, header_user_data, NULL);
if (expect_false(SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_NEEDS_FINALIZE))) {
srl_finalize_structure(aTHX_ dec);
}
srl_clear_decoder_body_state(aTHX_ dec); /* clean up for the main body decode */
}
else {
/* Either off in bitfield or no user data wanted, skip to end of header */
SRL_RDR_ASSERT_SPACE(dec->pbuf, header_len, " while reading header packet");
dec->buf.pos += header_len - 1; /* header_len includes bitfield */
}
}
else {
/* Skip header since we don't have any defined header-content in this
* protocol version. */
dec->buf.pos += header_len;
}
}
}
SRL_STATIC_INLINE void
srl_finalize_structure(pTHX_ srl_decoder_t *dec)
{
int nobless = SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_NO_BLESS_OBJECTS);
if (dec->weakref_av)
av_clear(dec->weakref_av);
if (dec->ref_stashes) {
/* The iterator could be leaked on exceptions if not for PTABLE_FLAG_AUTOCLEAN. */
PTABLE_ITER_t *it = PTABLE_iter_new_flags(dec->ref_stashes, PTABLE_FLAG_AUTOCLEAN);
PTABLE_ENTRY_t *ent;
/* We have gotten here without error, so bless all the objects.
* We defer to the end like this so that we only bless data structures
* if the entire deserialization completes. */
while ( NULL != (ent = PTABLE_iter_next(it)) ) {
HV *stash = (HV* )ent->value;
AV *ref_bless_av = (AV *) PTABLE_fetch(dec->ref_bless_av, ent->key);
I32 len;
if (expect_false( !stash || !ref_bless_av )) {
PTABLE_iter_free(it);
SRL_RDR_ERROR(dec->pbuf, "missing stash or ref_bless_av!");
}
for( len= av_len(ref_bless_av) + 1 ; len > 0 ; len-- ) {
SV* obj= av_pop(ref_bless_av); /*note that av_pop does NOT refcnt dec the sv*/
if (SvREFCNT(obj)>1) {
/* It is possible that someone handcrafts a hash with a key collision,
* which could trick us into effectively blessing an object and then
* calling DESTROY on it. So we track the refcount of the objects
* popped off the ref_bless_av, and only bless if their refcount *before*
* we refcount dec is higher than 1. If it is 1 then we just destroy the
* object.
* */
if (expect_true( obj )) {
#if USE_588_WORKAROUND
/* was blessed early, don't rebless */
#else
if (!nobless) {
if ( SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_READONLY_FLAGS) && SvREADONLY(SvRV(obj))) {
/* the referenced scalar was readonly, temporary
set it rw to bless its reference */
SvREADONLY_off(SvRV(obj));
sv_bless(obj, stash);
SvREADONLY_on(SvRV(obj));
} else {
sv_bless(obj, stash);
}
}
#endif
} else {
PTABLE_iter_free(it);
SRL_RDR_ERROR(dec->pbuf, "object missing from ref_bless_av array?");
}
} else {
warn("serialization contains a duplicated key, ignoring");
}
SvREFCNT_dec(obj);
}
}
PTABLE_iter_free(it);
}
}
/* PRIVATE UTILITY FUNCTIONS */
SRL_STATIC_INLINE void
srl_track_thawed(srl_decoder_t *dec, const U8 *track_pos, SV *sv)
{
if (!dec->ref_thawhash)
dec->ref_thawhash = PTABLE_new();
PTABLE_store(dec->ref_thawhash, (void *)(track_pos - dec->buf.body_pos), (void *)sv);
}
SRL_STATIC_INLINE SV *
srl_fetch_thawed(srl_decoder_t *dec, UV item)
{
if (dec->ref_thawhash) {
SV *sv= (SV *)PTABLE_fetch(dec->ref_thawhash, (void *)item);
return sv;
} else {
return NULL;
}
}
SRL_STATIC_INLINE void
srl_track_sv(pTHX_ srl_decoder_t *dec, const U8 *track_pos, SV *sv)
{
PTABLE_store(dec->ref_seenhash, (void *)(track_pos - dec->buf.body_pos), (void *)sv);
}
SRL_STATIC_INLINE SV *
srl_fetch_item(pTHX_ srl_decoder_t *dec, UV item, const char * const tag_name)
{
SV *sv= (SV *)PTABLE_fetch(dec->ref_seenhash, (void *)item);
#ifndef FOLLOW_REFERENCES_IF_NOT_STASHED
if (expect_false( !sv )) {
/*srl_ptable_debug_dump(aTHX_ dec->ref_seenhash);*/
SRL_RDR_ERRORf2(dec->pbuf, "%s(%"UVuf") references an unknown item", tag_name, item);
}
#endif
return sv;
}
/****************************************************************************
* PRIVATE WORKER SUBS FOR DEPARSING *
****************************************************************************/
SRL_STATIC_INLINE void
srl_alias_iv(pTHX_ srl_decoder_t *dec, SV **container, const U8 *track_it, IV iv)
{
SV *alias;
SV **av_array= AvARRAY(dec->alias_cache);
U32 ofs = iv + 16; /* we always cover from -16 up so we add 16 */
assert( IS_IV_ALIAS(dec,iv) );
if (!av_array[ofs] || av_array[ofs] == &PL_sv_undef) {
alias= newSViv(iv);
/* mark it as readonly so people dont try to modify it */
SvREADONLY_on(alias);
/* store it in the alias_cache array */
av_array[ofs]= alias;
} else {
alias= av_array[ofs];
}
SvREFCNT_inc(alias);
if (*container && *container != &PL_sv_undef)
SvREFCNT_dec(*container);
*container= alias;
if (track_it)
srl_track_sv(aTHX_ dec, track_it, alias);
}
SRL_STATIC_INLINE void
srl_setiv(pTHX_ srl_decoder_t *dec, SV *into, SV **container, const U8 *track_it, IV iv)
{
if ( expect_false( container && IS_IV_ALIAS(dec,iv) )) {
srl_alias_iv(aTHX_ dec, container, track_it, iv);
} else {
/* unroll sv_setiv() for the SVt_NULL case, which we will
* see regularly - this wins about 35% speedup for us
* but involve gratuitious intimacy with the internals.
* */
#ifdef FAST_IV
if ( SvTYPE(into) == SVt_NULL ) {
/* XXX: dont need to do this, we are null already */
/* SvFLAGS(into) &= ~SVTYPEMASK; */
assert(
(SVt_NULL == 0) &&
((SvFLAGS(into) & (SVTYPEMASK|SVf_OOK|SVf_OK|SVf_IVisUV|SVf_UTF8)) == 0)
);
SvANY(into) = (XPVIV*)((char*)&(into->sv_u.svu_iv) - STRUCT_OFFSET(XPVIV, xiv_iv));
/* replace this: */
/* SvIOK_only(into); */
/* with this: */
SvFLAGS(into) |= (SVt_IV | SVf_IOK | SVp_IOK);
SvIV_set(into, iv);
} else
#endif
{
sv_setiv(into, iv);
}
}
}
SRL_STATIC_INLINE void
srl_read_varint_into(pTHX_ srl_decoder_t *dec, SV* into, SV **container, const U8 *track_it)
{
UV uv= srl_read_varint_uv(aTHX_ dec->pbuf);
if (expect_true(uv <= (UV)IV_MAX)) {
srl_setiv(aTHX_ dec, into, container, track_it, (IV)uv);
} else {
/* grr, this is ridiculous! */
sv_setiv(into, 0);
SvIsUV_on(into);
SvUV_set(into, uv);
}
}
SRL_STATIC_INLINE IV
srl_read_zigzag_iv(pTHX_ srl_decoder_t *dec)
{
UV n= srl_read_varint_uv(aTHX_ dec->pbuf);
IV i= (n >> 1) ^ (-(n & 1));
return i;
}
SRL_STATIC_INLINE void
srl_read_zigzag_into(pTHX_ srl_decoder_t *dec, SV* into, SV **container, const U8 *track_it)
{
srl_setiv(aTHX_ dec, into, container, track_it, srl_read_zigzag_iv(aTHX_ dec));
}
SRL_STATIC_INLINE void
srl_read_string(pTHX_ srl_decoder_t *dec, int is_utf8, SV* into)
{
UV len= srl_read_varint_uv_length(aTHX_ dec->pbuf, " while reading string");
if (expect_false(is_utf8 && SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_VALIDATE_UTF8))) {
/* checks for invalid byte sequences. */
if (expect_false( !is_utf8_string((U8*)dec->buf.pos, len) )) {
SRL_RDR_ERROR(dec->pbuf, "Invalid UTF8 byte sequence");
}
}
sv_setpvn(into,(char *)dec->buf.pos,len);
if (is_utf8) {
SvUTF8_on(into);
} else {
SvUTF8_off(into);
}
dec->buf.pos+= len;
}
/* declare a union so that we are guaranteed the right alignment
* rules - this is required for e.g. ARM */
union myfloat {
U8 c[sizeof(long double)];
float f;
double d;
long double ld;
};
/* XXX Most (if not all?) non-x86 platforms are strict in their
* floating point alignment. So maybe this logic should be the other
* way: default to strict, and do sloppy only if x86? */
SRL_STATIC_INLINE void
srl_read_float(pTHX_ srl_decoder_t *dec, SV* into)
{
union myfloat val;
SRL_RDR_ASSERT_SPACE(dec->pbuf, sizeof(float), " while reading FLOAT");
#if SRL_USE_ALIGNED_LOADS_AND_STORES
Copy(dec->buf.pos,val.c,sizeof(float),U8);
#else
val.f= *((float *)dec->buf.pos);
#endif
sv_setnv(into, (NV)val.f);
dec->buf.pos+= sizeof(float);
}
SRL_STATIC_INLINE void
srl_read_double(pTHX_ srl_decoder_t *dec, SV* into)
{
union myfloat val;
SRL_RDR_ASSERT_SPACE(dec->pbuf, sizeof(double), " while reading DOUBLE");
#if SRL_USE_ALIGNED_LOADS_AND_STORES
Copy(dec->buf.pos,val.c,sizeof(double),U8);
#else
val.d= *((double *)dec->buf.pos);
#endif
sv_setnv(into, (NV)val.d);
dec->buf.pos+= sizeof(double);
}
SRL_STATIC_INLINE void
srl_read_long_double(pTHX_ srl_decoder_t *dec, SV* into)
{
union myfloat val;
SRL_RDR_ASSERT_SPACE(dec->pbuf, sizeof(long double), " while reading LONG_DOUBLE");
#if SRL_USE_ALIGNED_LOADS_AND_STORES
Copy(dec->buf.pos,val.c,sizeof(long double),U8);
#else
val.ld= *((long double *)dec->buf.pos);
#endif
sv_setnv(into, (NV)val.ld);
dec->buf.pos+= sizeof(long double);
}
SRL_STATIC_INLINE void
srl_read_array(pTHX_ srl_decoder_t *dec, SV *into, U8 tag) {
UV len;
if (tag) {
SV *referent= (SV *)newAV();
len= tag & 15;
SRL_sv_set_rv_to(into, referent);
into= referent;
DEPTH_INCREMENT(dec);
} else {
len= srl_read_varint_uv_count(aTHX_ dec->pbuf, " while reading ARRAY");
(void)SvUPGRADE(into, SVt_PVAV);
}
if (len) {
SV **av_array;
SV **av_end;
SRL_RDR_ASSERT_SPACE(dec->pbuf,len," while reading array contents, insufficient remaining tags for specified array size");
/* make sure the array has room */
av_extend((AV*)into, len-1);
/* set the size */
AvFILLp(into)= len - 1;
av_array= AvARRAY((AV*)into);
av_end= av_array + len;
for ( ; av_array < av_end ; av_array++) {
*av_array = FRESH_SV();
srl_read_single_value(aTHX_ dec, *av_array, av_array);
}
}
if (tag)
DEPTH_DECREMENT(dec);
}
#ifndef HV_FETCH_LVALUE
# define OLDHASH
# define IS_LVALUE 1
# define KEYLENTYPE IV
#else
# define KEYLENTYPE STRLEN
#endif
#ifndef HvRITER_set
# define HvRITER_set(sv,v) HvRITER(sv) = v
#endif
SRL_STATIC_INLINE void
srl_read_hash(pTHX_ srl_decoder_t *dec, SV* into, U8 tag) {
UV num_keys;
if (tag) {
SV *referent= (SV *)newHV();
num_keys= tag & 15;
SRL_sv_set_rv_to(into, referent);
into= referent;
DEPTH_INCREMENT(dec);
} else {
num_keys= srl_read_varint_uv_count(aTHX_ dec->pbuf, " while reading HASH");
(void)SvUPGRADE(into, SVt_PVHV);
}
/* in some versions of Perl HvRITER() is not properly set on an upgrade SV
* so we explicitly set it ourselves */
#ifdef FIXUP_RITER
HvRITER_set(into,-1);
#endif
/* Limit the maximum number of hash keys that we accept to whetever was configured */
if (expect_false( dec->max_num_hash_entries != 0 && num_keys > dec->max_num_hash_entries )) {
SRL_RDR_ERRORf2(dec->pbuf, "Got input hash with %u entries, but the configured maximum is just %u",
(int)num_keys, (int)dec->max_num_hash_entries);
}
SRL_RDR_ASSERT_SPACE(dec->pbuf,num_keys*2," while reading hash contents, insufficient remaining tags for number of keys specified");
HvSHAREKEYS_on(into); /* apparently required on older perls */
hv_ksplit((HV *)into, num_keys); /* make sure we have enough room */
/* NOTE: contents of hash are stored VALUE/KEY, reverse from normal perl
* storage, this is because it simplifies the hash storage logic somewhat */
for (; num_keys > 0 ; num_keys--) {
const U8 *from;
U8 tag;
SV **fetched_sv;
#ifndef OLDHASH
U32 flags= 0;
#endif
KEYLENTYPE key_len;
SRL_RDR_ASSERT_SPACE(dec->pbuf,1," while reading key tag byte for HASH");
tag= (*dec->buf.pos++)&127;
if (IS_SRL_HDR_SHORT_BINARY(tag)) {
key_len= (KEYLENTYPE)SRL_HDR_SHORT_BINARY_LEN_FROM_TAG(tag);
SRL_RDR_ASSERT_SPACE(dec->pbuf,key_len," while reading string/SHORT_BINARY key");
from= dec->buf.pos;
dec->buf.pos += key_len;
} else if (tag == SRL_HDR_BINARY) {
key_len= (KEYLENTYPE)srl_read_varint_uv_length(aTHX_ dec->pbuf, " while reading string/BINARY key");
SRL_RDR_ASSERT_SPACE(dec->pbuf,key_len," while reading binary key");
from= dec->buf.pos;
dec->buf.pos += key_len;
} else if (tag == SRL_HDR_STR_UTF8) {
key_len= (KEYLENTYPE)srl_read_varint_uv_length(aTHX_ dec->pbuf, " while reading UTF8 key");
SRL_RDR_ASSERT_SPACE(dec->pbuf,key_len," while reading string key");
from= dec->buf.pos;
dec->buf.pos += key_len;
#ifdef OLDHASH
key_len= -key_len;
#else
flags= HVhek_UTF8;
#endif
} else if (tag == SRL_HDR_COPY) {
UV ofs= srl_read_varint_uv_offset(aTHX_ dec->pbuf, " while reading COPY tag");
from= dec->buf.body_pos + ofs;
tag= *from++;
/* note we do NOT validate these items, as we have alread read them
* and if they were a problem we would not be here to process them! */
if (IS_SRL_HDR_SHORT_BINARY(tag)) {
key_len= (KEYLENTYPE)SRL_HDR_SHORT_BINARY_LEN_FROM_TAG(tag);
}
else
if (tag == SRL_HDR_BINARY) {
key_len = (KEYLENTYPE)S_read_varint_uv_length_char_ptr(
aTHX_ &from, dec->buf.end,
" while reading (byte) string length (via COPY)"
);
}
else
if (tag == SRL_HDR_STR_UTF8) {
key_len = (KEYLENTYPE)S_read_varint_uv_length_char_ptr(
aTHX_ &from, dec->buf.end,
" while reading UTF8-encoded string length (via COPY)"
);
#ifdef OLDHASH
key_len= -key_len;
#else
flags= HVhek_UTF8;
#endif
}
else {
SRL_RDR_ERROR_BAD_COPY(dec->pbuf, SRL_HDR_HASH);
}
} else {
SRL_RDR_ERROR_UNEXPECTED(dec->pbuf, tag, "a stringish type");
}
if (SvREADONLY(into)) {
SvREADONLY_off(into);
}
#ifdef OLDHASH
fetched_sv= hv_fetch((HV *)into, (char *)from, key_len, IS_LVALUE);
#else
fetched_sv= (SV **) hv_common((HV *)into, NULL, (char *)from, key_len, flags, HV_FETCH_LVALUE|HV_FETCH_JUST_SV, NULL, 0);
#endif
if (expect_false( !fetched_sv )) {
SRL_RDR_ERROR_PANIC(dec->pbuf, "failed to hv_store");
}
else
if ( expect_false( SvTYPE(*fetched_sv) != SVt_NULL ) ) {
/* sv_dump(*fetched_sv); */
SRL_RDR_ERRORf2(dec->pbuf, "duplicate key '%.*s' in hash", (int) key_len, (char *)from);
}
srl_read_single_value(aTHX_ dec, *fetched_sv, fetched_sv );
}
if (tag)
DEPTH_DECREMENT(dec);
}
SRL_STATIC_INLINE void
srl_read_refn(pTHX_ srl_decoder_t *dec, SV* into)
{
SV *referent;
U8 tag;
SRL_RDR_ASSERT_SPACE(dec->pbuf, 1, " while reading REFN referent");
tag= *(dec->buf.pos); /* Look ahead for special vars. */
if (tag == SRL_HDR_TRUE) {
dec->buf.pos++;
referent= &PL_sv_yes;
}
else if (tag == SRL_HDR_FALSE) {
dec->buf.pos++;
referent= &PL_sv_no;
}
/*
* Note the below is guarded by an option as we have use SRL_HDR_UNDEF
* also to represent "any SV which is undef", and using to represent
* true PL_sv_undef will break things.
*
* We need a new, different tag for true perl undef.
*
*/
else
if (
( tag == SRL_HDR_CANONICAL_UNDEF )
||
( SRL_DEC_HAVE_OPTION(dec,SRL_F_DECODER_USE_UNDEF) && tag == SRL_HDR_UNDEF )
) {
dec->buf.pos++;
referent= &PL_sv_undef;
}
else {
referent= FRESH_SV();
SvTEMP_off(referent);
tag = 0;
}
SRL_sv_set_rv_to(into, referent);
if (!tag) {
DEPTH_INCREMENT(dec);
srl_read_single_value(aTHX_ dec, referent, NULL);
DEPTH_DECREMENT(dec);
}
}
SRL_STATIC_INLINE SV *
srl_follow_refp_alias_reference(pTHX_ srl_decoder_t *dec, UV offset)
{
SV* into = sv_2mortal(FRESH_SV());
srl_reader_char_ptr orig_pos = dec->buf.pos;
srl_reader_char_ptr new_pos = dec->buf.body_pos + offset;
if (new_pos >= orig_pos) {
SRL_RDR_ERROR(dec->pbuf, "Corrupted packed. Reference offset points forward!");
}
dec->buf.pos = new_pos;
srl_read_single_value(aTHX_ dec, into, NULL);
dec->buf.pos = orig_pos;
return into;
}
SRL_STATIC_INLINE AV *
srl_follow_objectv_reference(pTHX_ srl_decoder_t *dec, UV offset)
{
AV *av= NULL;
srl_reader_char_ptr orig_pos = dec->buf.pos;
srl_reader_char_ptr new_pos = dec->buf.body_pos + offset;
if (new_pos >= orig_pos) {
SRL_RDR_ERROR(dec->pbuf, "Corrupted packed. Reference offset points forward!");
}
SRL_ASSERT_REF_PTR_TABLES(dec); /* init dec->ref_stashes and dec->ref_bless_av */
dec->buf.pos = new_pos;
/* call srl_read_object() with read_class_name_only=1 */
/* into and obj_tag are not used in this case */
srl_read_object(aTHX_ dec, NULL, 0, 1);
av= (AV *)PTABLE_fetch(dec->ref_bless_av, (void *)offset);
dec->buf.pos = orig_pos;
return av;
}
SRL_STATIC_INLINE void
srl_read_refp(pTHX_ srl_decoder_t *dec, SV* into)
{
/* something we did before */
UV item= srl_read_varint_uv_offset(aTHX_ dec->pbuf, " while reading REFP tag");
SV *thawed= srl_fetch_thawed(dec, item);
SV *referent;
if (thawed) {
sv_setsv(into, thawed);
return;
}
referent= srl_fetch_item(aTHX_ dec, item, "REFP");
#ifdef FOLLOW_REFERENCES_IF_NOT_STASHED
if (referent == NULL)
referent = srl_follow_refp_alias_reference(aTHX_ dec, item);
#endif
(void)SvREFCNT_inc(referent);
SRL_sv_set_rv_to(into, referent);
#if USE_588_WORKAROUND
/* See 'define USE_588_WORKAROUND' above for a discussion of what this does. */
if (SvOBJECT(referent)) {
HV *stash = SvSTASH(referent);
if (Gv_AMG(stash))
SvAMAGIC_on(into);
}
#endif
}
SRL_STATIC_INLINE void
srl_read_weaken(pTHX_ srl_decoder_t *dec, SV* into)
{
SV* referent;
/* TODO This really just wants a subset of the states that srl_read_single_value covers, right?
* Optimization opportunity? Or robustness against invalid packets issue? */
srl_read_single_value(aTHX_ dec, into, NULL);
if (expect_false( !SvROK(into) ))
SRL_RDR_ERROR(dec->pbuf, "WEAKEN op");
referent= SvRV(into);
/* we have to be careful to not allow the referent's refcount
* to go to zero in the process of us weakening the ref.
* For instance this may be aliased or reused later by a non-weakref
* which will "fix" the refcount, however we need to be able to deserialize
* in the opposite order, so if the referent's refcount is 1
* we increment it and stuff it in the weakref_av before we call
* sv_rvweaken(), right before we exit we clear any items from
* that array, which does the REFCNT_dec for us, and everything
* works out ok. */
if (expect_true( SvREFCNT(referent)==1 )) {
if (expect_false( !dec->weakref_av ))
dec->weakref_av= newAV();
av_push(dec->weakref_av, SvREFCNT_inc(referent));
SRL_DEC_SET_OPTION(dec, SRL_F_DECODER_NEEDS_FINALIZE);
}
/* If read-only reference, set to rw only to weaken it, otherwise "Modification of a read-only value attempted". */
if ( SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_READONLY_FLAGS) && SvREADONLY(into)) {
SvREADONLY_off(into);
sv_rvweaken(into);
SvREADONLY_on(into);
} else {
sv_rvweaken(into);
}
}
SRL_STATIC_INLINE void
srl_read_objectv(pTHX_ srl_decoder_t *dec, SV* into, U8 obj_tag)
{
AV *av= NULL;
STRLEN ofs;
if (expect_false( SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_REFUSE_OBJECTS) ))
SRL_RDR_ERROR_REFUSE_OBJECT(dec->pbuf);
ofs= srl_read_varint_uv_offset(aTHX_ dec->pbuf, " while reading OBJECTV(_FREEZE) classname");
if (expect_false( !dec->ref_bless_av )) {
#ifdef FOLLOW_REFERENCES_IF_NOT_STASHED
SRL_ASSERT_REF_PTR_TABLES(dec); /* init dec->ref_stashes and dec->ref_bless_av */
#else
SRL_RDR_ERROR(dec->pbuf, "Corrupted packet. OBJECTV(_FREEZE) used without "
"preceding OBJECT(_FREEZE) to define classname");
#endif
}
av= (AV *)PTABLE_fetch(dec->ref_bless_av, (void *)ofs);
if (expect_false( NULL == av )) {
#ifdef FOLLOW_REFERENCES_IF_NOT_STASHED
av = srl_follow_objectv_reference(aTHX_ dec, (UV) ofs);
if (expect_false( NULL == av ))
#endif
SRL_RDR_ERRORf1(dec->pbuf, "Corrupted packet. OBJECTV(_FREEZE) references unknown classname offset: %"UVuf, (UV)ofs);
}
/* checking tag: SRL_HDR_OBJECTV_FREEZE or SRL_HDR_OBJECTV? */
if (expect_false( obj_tag == SRL_HDR_OBJECTV_FREEZE )) {
HV *class_stash= (HV *) PTABLE_fetch(dec->ref_stashes, (void *)ofs);
if (expect_false( class_stash == NULL ))
SRL_RDR_ERROR(dec->pbuf, "Corrupted packet. OBJECTV(_FREEZE) used without "
"preceding OBJECT(_FREEZE) to define classname");
srl_read_frozen_object(aTHX_ dec, class_stash, into);
} else {
/* SRL_HDR_OBJECTV, not SRL_HDR_OBJECTV_FREEZE */
/* now deparse the thing we are going to bless */
srl_read_single_value(aTHX_ dec, into, NULL);
/* and also stuff it into the av - we dont have to do any more book-keeping */
av_push(av, SvREFCNT_inc(into));
#if USE_588_WORKAROUND
{
/* See 'define USE_588_WORKAROUND' above for a discussion of what this does. */
HV *class_stash= PTABLE_fetch(dec->ref_stashes, (void *)ofs);
if (expect_false( class_stash == NULL ))
SRL_RDR_ERROR(dec->pbuf, "Corrupted packet. OBJECTV(_FREEZE) used without "
"preceding OBJECT(_FREEZE) to define classname");
if (!SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_NO_BLESS_OBJECTS))
sv_bless(into, class_stash);
}
#endif
}
}
SRL_STATIC_INLINE void
srl_read_object(pTHX_ srl_decoder_t *dec, SV* into, U8 obj_tag, int read_class_name_only)
{
HV *class_stash= NULL;
AV *av= NULL;
STRLEN storepos= 0;
UV ofs= 0;
I32 flags= GV_ADD;
U8 tag;
U32 key_len = 0;
const U8 *from = NULL;
if (expect_false( SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_REFUSE_OBJECTS) ))
SRL_RDR_ERROR_REFUSE_OBJECT(dec->pbuf);
/* Now find the class name - first check if this is a copy op
* this is bit tricky, as we could have a copy of a raw string.
* We could also have a copy of a previously mentioned class
* name. We have to handle both, which leads to some non-linear
* code flow in the below code. */
SRL_RDR_ASSERT_SPACE(dec->pbuf,1," while reading classname tag");
/* Now read the class name and cache it */
storepos= SRL_RDR_BODY_POS_OFS(dec->pbuf);
tag= *dec->buf.pos++;
if (IS_SRL_HDR_SHORT_BINARY(tag)) {
key_len= SRL_HDR_SHORT_BINARY_LEN_FROM_TAG(tag);
from= dec->buf.pos;
SRL_RDR_ASSERT_SPACE(dec->pbuf,key_len," while reading short binary");
dec->buf.pos += key_len;
}
else
if (tag == SRL_HDR_STR_UTF8) {
key_len= srl_read_varint_uv_length(aTHX_ dec->pbuf, " while reading UTF8 class name");
flags = flags | SVf_UTF8;
from= dec->buf.pos;
SRL_RDR_ASSERT_SPACE(dec->pbuf,key_len," while reading utf8 string");
dec->buf.pos += key_len;
}
else
if (tag == SRL_HDR_BINARY) {
key_len= srl_read_varint_uv_length(aTHX_ dec->pbuf, " while reading string/BINARY class name");
from= dec->buf.pos;
SRL_RDR_ASSERT_SPACE(dec->pbuf,key_len," while reading binary");
dec->buf.pos += key_len;
}
else
if (tag == SRL_HDR_COPY) {
ofs= srl_read_varint_uv_offset(aTHX_ dec->pbuf, " while reading COPY class name");
storepos= ofs;
/* if this string was seen before as part of a classname then we expect
* a stash available below. However it might have been serialized as a key
* or something like that, which would mean we dont have an entry in ref_stashes
* anymore. So first we check if we have a stash. If we do, then we can avoid
* some work. */
if (expect_true( dec->ref_stashes != NULL )) {
class_stash= (HV *) PTABLE_fetch(dec->ref_stashes, (void *)ofs);
}
/* Check if we actually got a class_stash back. If we didn't then we need
* to deserialize the class name */
if (!class_stash) {
from= dec->buf.body_pos + ofs;
tag= *from++;
/* Note we do NOT validate these items, as we have already read them
* and if they were a problem we would not be here to process them! */
if (IS_SRL_HDR_SHORT_BINARY(tag)) {
key_len= SRL_HDR_SHORT_BINARY_LEN_FROM_TAG(tag);
}
else
if (tag == SRL_HDR_BINARY) {
key_len = (KEYLENTYPE)S_read_varint_uv_length_char_ptr(
aTHX_ &from, dec->buf.end,
" while reading (byte) length for class name (via COPY)"
);
}
else
if (tag == SRL_HDR_STR_UTF8) {
key_len = (KEYLENTYPE)S_read_varint_uv_length_char_ptr(
aTHX_ &from, dec->buf.end,
" while reading UTF8 string length for class name (via COPY)"
);
flags = flags | SVf_UTF8;
if (!is_utf8_string(from, key_len)) {
SRL_RDR_ERROR_PANIC(dec->pbuf, "utf8 flagged classname is not actually utf8");
}
}
else {
SRL_RDR_ERROR_BAD_COPY(dec->pbuf, SRL_HDR_OBJECT);
}
}
} else {
SRL_RDR_ERROR_UNEXPECTED(dec->pbuf, tag, "a class name");
}
/* At this point we may or may not have a class stash. If they used a Copy there
* is a decent chance we do. */
SRL_ASSERT_REF_PTR_TABLES(dec);
if (!class_stash) {
/* no class stash - so we need to look it up and then store it away for future use */
class_stash= gv_stashpvn((char *)from, key_len, flags);
PTABLE_store(dec->ref_stashes, (void *)storepos, (void *)class_stash);
/* Since this is the first time we have seen this stash then it is the first time
* that we have stored an item in the ref_bless_av hash as well. So create a new one
* and store it away. */
av= newAV();
sv_2mortal((SV*)av);
PTABLE_store(dec->ref_bless_av, (void *)storepos, (void *)av);
} else {
/* we have a class stash so we should have a ref_bless_av as well. */
av= (AV *)PTABLE_fetch(dec->ref_bless_av, (void *)storepos);
if ( !av )
SRL_RDR_ERRORf1(dec->pbuf, "Panic, no ref_bless_av for %"UVuf, (UV)storepos);
}
#ifdef FOLLOW_REFERENCES_IF_NOT_STASHED
/* at this point we have class name read and have coressponding records in
* dec->dec->ref_stashes and dec->ref_bless_av. So, we can simply fetch
* from hashes outside this function. The code */
if (read_class_name_only) return;
#else
assert(into != NULL);
assert(obj_tag != 0);
#endif
if (expect_false( obj_tag == SRL_HDR_OBJECT_FREEZE )) {
srl_read_frozen_object(aTHX_ dec, class_stash, into);
} else {
/* We now have a stash so we /could/ bless... except that
* we don't actually want to do so right now. We want to defer blessing
* until the full packet has been read. Yes it is more overhead, but
* we really dont want to trigger DESTROY methods from a partial
* deparse. So we insert the item into an array to be blessed later. */
SRL_DEC_SET_OPTION(dec, SRL_F_DECODER_NEEDS_FINALIZE);
av_push(av, SvREFCNT_inc(into));
/* now deparse the thing we are going to bless */
srl_read_single_value(aTHX_ dec, into, NULL);
#if USE_588_WORKAROUND
/* See 'define USE_588_WORKAROUND' above for a discussion of what this does. */
if (!SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_NO_BLESS_OBJECTS))
sv_bless(into, class_stash);
#endif
}
}
/* Invoke a THAW callback on the given class. Pass in the next item in the
* decoder stream. This is implementing the FREEZE/THAW part of
* SRL_HDR_OBJECT_FREEZE and SRL_HDR_OBJECTV_FREEZE. */
SRL_STATIC_INLINE void
srl_read_frozen_object(pTHX_ srl_decoder_t *dec, HV *class_stash, SV *into)
{
GV *method = gv_fetchmethod_autoload(class_stash, "THAW", 0);
char *classname = HvNAME(class_stash);
SV* referent;
SV *replacement;
/* At this point in the input stream we should have REFN WHATEVER. The WHATEVER
* may be referenced from multiple RV's in the data structure, which means that
* srl_read_single_value() will cache the *unthawed* representation when we finally
* process it. So we need to do some special bookkeeping here and then overwrite
* that representation in the refs hash.
*/
const unsigned char *fixup_pos= dec->buf.pos + 1; /* get the tag for the WHATEVER */
if (expect_false( method == NULL ))
SRL_RDR_ERRORf1(dec->pbuf, "No THAW method defined for class '%s'", HvNAME(class_stash));
srl_read_single_value(aTHX_ dec, into, NULL);
/* Assert that we got a top level array ref as the spec requires.
* Not throwing an exception here violates expectations down the line and
* can lead to segfaults. */
if (expect_false( !SvROK(into) || SvTYPE(SvRV(into)) != SVt_PVAV ))
SRL_RDR_ERROR(dec->pbuf, "Corrupted packet. OBJECT(V)_FREEZE used without "
"being followed by an array reference");
{
int count;
AV *arg_av= (AV*)SvRV(into);
int arg_av_len = av_len(arg_av)+1;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, 3);
/* TODO Consider more caching for some of this */
PUSHs(sv_2mortal(newSVpvn(classname, strlen(classname))));
/* FIXME do not recreate the following SV. That's dumb and wasteful! - so long as it doesnt get modified! */
PUSHs(sv_2mortal(newSVpvs("Sereal")));
/* Push the args into the stack */
for (count=0 ; count < arg_av_len; count++) {
PUSHs((SV*)*av_fetch(arg_av, count, 0));
}
PUTBACK;
count = call_sv((SV *)GvCV(method), G_SCALAR);
/* TODO explore method lookup caching */
SPAGAIN;
if (expect_true( count == 1 )) {
replacement = POPs;
SvREFCNT_inc(replacement);
} else {
replacement = &PL_sv_undef;
}
/* If count is not 1, then it's 0. Then into is already undef. */
PUTBACK;
FREETMPS;
LEAVE;
}
/* At this point "into" is an SvRV pointing at the *unthawed* representation.
* This means we need to a) remove the old unthawed item and dispose of it
* and b) make "into" point at the replacement, and c) if necessary store the
* replacement in the sv tracking hash so that future references to this item
* point at the *thawed* version. */
if (SvROK(replacement)) {
SV *tmpsv= replacement;
replacement= SvRV(tmpsv);
SvREFCNT_inc(replacement);
SvREFCNT_dec(tmpsv);
referent= SvRV(into);
SvRV_set(into, replacement);
SvREFCNT_dec(referent);
if (*fixup_pos & SRL_HDR_TRACK_FLAG)
srl_track_sv(aTHX_ dec, fixup_pos, replacement);
} else if (*fixup_pos & SRL_HDR_TRACK_FLAG) {
srl_track_thawed(dec, fixup_pos, replacement);
sv_setsv(into, replacement);
}
}
SRL_STATIC_INLINE void
srl_read_reserved(pTHX_ srl_decoder_t *dec, U8 tag, SV* into)
{
const UV len = srl_read_varint_uv_length(aTHX_ dec->pbuf, " while reading reserved");
(void)tag; /* unused as of now */
dec->buf.pos += len; /* discard */
sv_setsv(into, &PL_sv_undef);
}
#if ((PERL_VERSION > 10) || (PERL_VERSION == 10 && PERL_SUBVERSION > 1 ))
# define MODERN_REGEXP
# define REGEXP_HAS_P_MODIFIER
# pragma message ( "MODERN_REGEXP" )
#elif PERL_VERSION == 10
# define TRANSITION_REGEXP
# define REGEXP_HAS_P_MODIFIER
# pragma message ( "TRANSITION_REGEXP" )
#else
# define OLD_REGEXP
# pragma message ( "OLD_REGEXP" )
#endif
SRL_STATIC_INLINE void
srl_read_regexp(pTHX_ srl_decoder_t *dec, SV* into)
{
SV *sv_pat= FRESH_SV();
srl_read_single_value(aTHX_ dec, sv_pat, NULL);
SRL_RDR_ASSERT_SPACE(dec->pbuf, 1, " while reading regexp modifer tag");
/* For now we will serialize the flags as ascii strings. Maybe we should use
* something else but this is easy to debug and understand - since the modifiers
* are tagged it doesn't matter much, we can add other tags later */
if ( expect_true( IS_SRL_HDR_SHORT_BINARY(*dec->buf.pos) ) ) {
U8 mod_len= SRL_HDR_SHORT_BINARY_LEN_FROM_TAG(*dec->buf.pos++);
U32 flags= 0;
SRL_RDR_ASSERT_SPACE(dec->pbuf, mod_len, " while reading regexp modifiers");
while (mod_len > 0) {
mod_len--;
switch (*dec->buf.pos++) {
case 'm':
flags= flags | PMf_MULTILINE;
break;
case 's':
flags= flags | PMf_SINGLELINE;
break;
case 'i':
flags= flags | PMf_FOLD;
break;
case 'x':
flags= flags | PMf_EXTENDED;
break;
#ifdef REGEXP_HAS_P_MODIFIER
case 'p':
flags = flags | PMf_KEEPCOPY;
break;
#endif
default:
SRL_RDR_ERROR(dec->pbuf, "bad modifier");
break;
}
}
#ifdef MODERN_REGEXP
{
/* This is ugly. We have to swap out the insides of our SV
* with the one we get back from CALLREGCOMP, as there is no
* way to get it to fill our SV.
*
* As far as I understand this works because of how the SV
* is laid out. Needs to be verified with someone who knows
* better.
*/
/* compile the regex */
SV *referent= (SV*)CALLREGCOMP(sv_pat, flags);
SV tmp;
/* make sure the SV came from us (it should) and
* is bodyless */
assert( SvTYPE(into) == SVt_NULL );
#define SWAP_DEBUG 0
if (SWAP_DEBUG) { warn("before swap:"); sv_dump(into); sv_dump(referent); }
/* Swap the contents of the two heads. */
Copy(into, &tmp, 1, SV);
Copy((SV*)referent, into, 1, SV);
Copy(&tmp, (SV*)referent, 1, SV);
SvREFCNT(referent)= SvREFCNT(into);
SvREFCNT(into)= SvREFCNT(&tmp);
if (SWAP_DEBUG) { warn("after swap:"); sv_dump(into); sv_dump(referent); }
SvREFCNT_dec(sv_pat); /* I think we need this or we leak */
/* and now throw away the head we got from the regexp engine. */
SvREFCNT_dec(referent);
}
#elif defined( TRANSITION_REGEXP )
{
REGEXP *referent = CALLREGCOMP(aTHX_ sv_pat, flags);
SvREFCNT_dec(sv_pat);
sv_magic( into, (SV*)referent, PERL_MAGIC_qr, 0, 0);
SvFLAGS( into ) |= SVs_SMG;
}
#else
{
PMOP pm; /* grr */
STRLEN pat_len;
REGEXP *re;
char *pat= SvPV(sv_pat, pat_len);
Zero(&pm,1,PMOP);
pm.op_pmdynflags= SvUTF8(sv_pat) ? PMdf_CMP_UTF8 : 0;
pm.op_pmflags= flags;
re= CALLREGCOMP(aTHX_ pat, pat + pat_len, &pm);
SvREFCNT_dec(sv_pat);
sv_magic( into, (SV*)re, PERL_MAGIC_qr, 0, 0);
SvFLAGS( into ) |= SVs_SMG;
}
#endif
}
else {
SRL_RDR_ERROR(dec->pbuf, "Expecting SRL_HDR_SHORT_BINARY for modifiers of regexp");
}
}
SRL_STATIC_INLINE SV *
srl_read_extend(pTHX_ srl_decoder_t *dec, SV* into)
{
/* FIXME unimplemented!!! */
SRL_RDR_ERROR_UNIMPLEMENTED(dec->pbuf, SRL_HDR_EXTEND,"EXTEND");
return into;
}
SRL_STATIC_INLINE void
srl_read_copy(pTHX_ srl_decoder_t *dec, SV* into)
{
UV item= srl_read_varint_uv_offset(aTHX_ dec->pbuf, " while reading COPY tag");
if (expect_false( dec->save_pos )) {
SRL_RDR_ERRORf1(dec->pbuf, "COPY(%d) called during parse", (int)item);
}
if (expect_false( (IV)item > dec->buf.end - dec->buf.start )) {
SRL_RDR_ERRORf1(dec->pbuf, "COPY(%d) points out of packet", (int)item);
}
dec->save_pos= dec->buf.pos;
dec->buf.pos= dec->buf.body_pos + item;
srl_read_single_value(aTHX_ dec, into, NULL);
dec->buf.pos= dec->save_pos;
dec->save_pos= 0;
}
/****************************************************************************
* MAIN DISPATCH SUB - ALL ROADS LEAD HERE *
****************************************************************************/
void
srl_decode_single_value(pTHX_ srl_decoder_t *dec, SV* into, SV** container)
{
srl_read_single_value(aTHX_ dec, into, container);
}
SRL_STATIC_INLINE void
srl_read_single_value(pTHX_ srl_decoder_t *dec, SV* into, SV** container)
{
STRLEN len;
U8 tag;
int is_ref = 0;
const U8 *track_it = NULL;
read_again:
if (expect_false( SRL_RDR_DONE(dec->pbuf) ))
SRL_RDR_ERROR(dec->pbuf, "unexpected end of input stream while expecting a single value");
tag= *dec->buf.pos++;
read_tag:
switch (tag) {
CASE_SRL_HDR_POS:
srl_setiv(aTHX_ dec, into, container, track_it, (IV)tag);
break;
CASE_SRL_HDR_NEG:
srl_setiv(aTHX_ dec, into, container, track_it, (IV)(tag - 32));
break;
CASE_SRL_HDR_SHORT_BINARY:
len= (STRLEN)SRL_HDR_SHORT_BINARY_LEN_FROM_TAG(tag);
SRL_RDR_ASSERT_SPACE(dec->pbuf, len, " while reading ascii string");
sv_setpvn(into,(char*)dec->buf.pos,len);
dec->buf.pos += len;
break;
CASE_SRL_HDR_HASHREF: srl_read_hash(aTHX_ dec, into, tag); is_ref = 1; break;
CASE_SRL_HDR_ARRAYREF: srl_read_array(aTHX_ dec, into, tag); is_ref = 1; break;
case SRL_HDR_VARINT: srl_read_varint_into(aTHX_ dec, into, container, track_it); break;
case SRL_HDR_ZIGZAG: srl_read_zigzag_into(aTHX_ dec, into, container, track_it); break;
case SRL_HDR_FLOAT: srl_read_float(aTHX_ dec, into); break;
case SRL_HDR_DOUBLE: srl_read_double(aTHX_ dec, into); break;
case SRL_HDR_LONG_DOUBLE: srl_read_long_double(aTHX_ dec, into); break;
case SRL_HDR_TRUE: sv_setsv(into, &PL_sv_yes); break;
case SRL_HDR_FALSE: sv_setsv(into, &PL_sv_no); break;
case SRL_HDR_CANONICAL_UNDEF: /* fallthrough (XXX: is this right?)*/
case SRL_HDR_UNDEF:
{
if (container && SRL_DEC_HAVE_OPTION(dec,SRL_F_DECODER_USE_UNDEF)){
SvREFCNT_dec(into);
*container= &PL_sv_undef;
if ( track_it )
srl_track_sv(aTHX_ dec, track_it, *container);
} else {
sv_setsv(into, &PL_sv_undef);
}
}
break;
case SRL_HDR_BINARY: srl_read_string(aTHX_ dec, 0, into); break;
case SRL_HDR_STR_UTF8: srl_read_string(aTHX_ dec, 1, into); break;
case SRL_HDR_WEAKEN: srl_read_weaken(aTHX_ dec, into); is_ref=1; break;
case SRL_HDR_REFN: srl_read_refn(aTHX_ dec, into); is_ref=1; break;
case SRL_HDR_REFP: srl_read_refp(aTHX_ dec, into); is_ref=1; break;
case SRL_HDR_OBJECT_FREEZE:
case SRL_HDR_OBJECT: srl_read_object(aTHX_ dec, into, tag, 0); is_ref=1; break;
case SRL_HDR_OBJECTV_FREEZE:
case SRL_HDR_OBJECTV: srl_read_objectv(aTHX_ dec, into, tag); is_ref=1; break;
case SRL_HDR_COPY: srl_read_copy(aTHX_ dec, into); break;
case SRL_HDR_EXTEND: srl_read_extend(aTHX_ dec, into); break;
case SRL_HDR_HASH: srl_read_hash(aTHX_ dec, into, 0); break;
case SRL_HDR_ARRAY: srl_read_array(aTHX_ dec, into, 0); break;
case SRL_HDR_REGEXP: srl_read_regexp(aTHX_ dec, into); break;
case SRL_HDR_ALIAS:
{
UV offset;
SV *alias;
if (!container)
SRL_RDR_ERROR(dec->pbuf, "ALIAS tag not inside container, corrupt packet?");
offset= srl_read_varint_uv_offset(aTHX_ dec->pbuf," while reading ALIAS tag");
alias= srl_fetch_item(aTHX_ dec, offset, "ALIAS");
#ifdef FOLLOW_REFERENCES_IF_NOT_STASHED
if (!alias) alias= srl_follow_refp_alias_reference(aTHX_ dec, offset);
#endif
SvREFCNT_inc(alias);
SvREFCNT_dec(into);
*container= alias;
if (track_it)
srl_track_sv(aTHX_ dec, track_it, alias);
return;
}
break;
case SRL_HDR_PAD: /* no op */
while (SRL_RDR_NOT_DONE(dec->pbuf) && *dec->buf.pos == SRL_HDR_PAD)
dec->buf.pos++;
goto read_again;
break;
default:
if (tag & SRL_HDR_TRACK_FLAG) {
tag= tag & ~SRL_HDR_TRACK_FLAG;
track_it = dec->buf.pos-1;
srl_track_sv(aTHX_ dec, track_it, into);
goto read_tag;
} else {
SRL_RDR_ERROR_UNEXPECTED(dec->pbuf, tag, " single value");
}
break;
}
/* they want us to set all SVs readonly, or only the non-ref */
#define SUPPORT_READONLY 1
#if SUPPORT_READONLY
if ( expect_false(dec->flags_readonly) )
{
if (
dec->flags_readonly == 1 || !is_ref
) {
if (is_ref && !SvREADONLY(SvRV(into)) ) {
SvREADONLY_on(SvRV(into));
}
if (!SvREADONLY(into)) {
SvREADONLY_on(into);
}
}
}
#endif
return;
}