#define _CRT_SECURE_NO_DEPRECATE /* Win32 compilers close eyes... */ #define PERL_NO_GET_CONTEXT #undef PERL_IMPLICIT_SYS /* Sigsetjmp will not work under this */ #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" #ifndef PERL_UNUSED_VAR # define PERL_UNUSED_VAR(var) if (0) var = var #endif #ifndef STATIC_INLINE /* a public perl API from 5.13.4 */ # if defined(__GNUC__) || defined(__cplusplus__) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) # define STATIC_INLINE static inline # else # define STATIC_INLINE static # endif #endif /* STATIC_INLINE */ #ifndef inline /* don't like borgs definitions */ /* inline is keyword for STDC compiler */ # if defined(__GNUC__) || defined(__cplusplus__) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) # else # if defined(WIN32) && defined(_MSV) /* Microsoft Compiler */ # define inline _inline # else # define inline # endif # endif #endif /* inline */ #define MARKER3_UNDEF '\x00' #define MARKER3_NULL '\x01' #define MARKER3_FALSE '\x02' #define MARKER3_TRUE '\x03' #define MARKER3_INTEGER '\x04' #define MARKER3_DOUBLE '\x05' #define MARKER3_STRING '\x06' #define MARKER3_XML_DOC '\x07' #define MARKER3_DATE '\x08' #define MARKER3_ARRAY '\x09' #define MARKER3_OBJECT '\x0a' #define MARKER3_XML '\x0b' #define MARKER3_BYTEARRAY '\x0c' #define MARKER3_AMF_PLUS '\x11' #define MARKER0_NUMBER '\x00' #define MARKER0_BOOLEAN '\x01' #define MARKER0_STRING '\x02' #define MARKER0_OBJECT '\x03' #define MARKER0_CLIP '\x04' #define MARKER0_UNDEFINED '\x05' #define MARKER0_NULL '\x06' #define MARKER0_REFERENCE '\x07' #define MARKER0_ECMA_ARRAY '\x08' #define MARKER0_OBJECT_END '\x09' #define MARKER0_STRICT_ARRAY '\x0a' #define MARKER0_DATE '\x0b' #define MARKER0_LONG_STRING '\x0c' #define MARKER0_UNSUPPORTED '\x0d' #define MARKER0_RECORDSET '\x0e' #define MARKER0_XML_DOCUMENT '\x0f' #define MARKER0_TYPED_OBJECT '\x10' #define MARKER0_AMF_PLUS '\x11' #define ERR_EOF 1 #define ERR_AMF0_REF 2 #define ERR_MARKER 3 #define ERR_BAD_OBJECT 4 #define ERR_OVERFLOW 5 #define ERR_UNIMPLEMENTED 6 #define ERR_BAD_STRING_REF 7 #define ERR_BAD_DATE_REF 8 #define ERR_BAD_OBJECT_REF 9 #define ERR_BAD_ARRAY_REF 10 #define ERR_BAD_STRING_REF_UNUSED 11 #define ERR_BAD_TRAIT_REF 12 #define ERR_BAD_XML_REF 13 #define ERR_BAD_BYTEARRAY_REF 14 #define ERR_EXTRA_BYTE 15 #define ERR_INT_OVERFLOW 16 #define ERR_RECURRENT_OBJECT 17 #define ERR_BAD_REFVAL 18 #define ERR_INTERNAL 19 #define ERR_ARRAY_TOO_BIG 20 #define ERR_BAD_OPTION 21 #define OPT_STRICT 1 #define OPT_DECODE_UTF8 2 #define OPT_ENCODE_UTF8 4 #define OPT_RAISE_ERROR 8 #define OPT_MILLSEC_DATE 16 #define OPT_PREFER_NUMBER 32 #define OPT_JSON_BOOLEAN 64 #define OPT_MAPPER 128 #define OPT_TARG 256 #define EXPERIMENT1 #define AMF0_VERSION 0 #define AMF3_VERSION 3 #define STR_EMPTY '\x01' #define TRACE(ELEM) PerlIO_printf( PerlIO_stderr(), ELEM); #undef TRACE #define TRACE(ELEM) ; #if BYTEORDER == 0x1234 #define GAX "LIT" #define GET_NBYTE(ALL, IPOS, TYPE) (ALL - 1 - IPOS) #else #if BYTEORDER == 0x12345678 #define GAX "LIT" #define GET_NBYTE(ALL, IPOS, TYPE) (ALL - 1 - IPOS) #else #if BYTEORDER == 0x87654321 #define GAX "BIG" #define GET_NBYTE(ALL, IPOS, TYPE) (sizeof(TYPE) -ALL + IPOS) #else #if BYTEORDER == 0x4321 #define GAX "BIG" #define GET_NBYTE(ALL, IPOS, TYPE) (sizeof(TYPE) -ALL + IPOS) #else #error Unknown byteorder. Please append your byteorder to Storable/AMF.xs #endif #endif #endif #endif #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) #define SIGN_BOOL_APPLY( obj, sign, mask ) ( sign > 0 ? obj|=mask : sign <0 ? obj&=~mask : 0 ) #define DEFAULT_MASK (OPT_PREFER_NUMBER|OPT_TARG) char *error_messages[] = { "ERR_EOF", "ERR_BAD_AMF0_REF", "ERR_MARKER", "ERR_BAD_OBJECT", "ERR_OVERFLOW", "ERR_UNIMPLEMENTED", "ERR_BAD_STRING_REF", "ERR_BAD_DATE_REF", "ERR_BAD_OBJECT_REF", "ERR_BAD_ARRAY_REF", "ERR_BAD_STRING_REF_UNUSED", "ERR_BAD_TRAIT_REF", "ERR_BAD_XML_REF", "ERR_BAD_BYTEARRAY_REF", "ERR_EXTRA_BYTE", "ERR_INT_OVERFLOW", "ERR_RECURRENT_OBJECT", "ERR_BAD_REFVAL", "ERR_INTERNAL", "ERR_ARRAY_TOO_BIG", "ERR_BAD_OPTION", 0 }; struct io_amf_option; /*#define TRACE0 */ struct amf3_restore_point{ int offset_buffer; int offset_object; int offset_trait; int offset_string; int arr_max; }; struct io_struct{ unsigned char * ptr; unsigned char * pos; unsigned char * end; SV * sv_buffer; int buffer_step_inc; int arr_max; Sigjmp_buf target_error; int error_code; AV *arr_string; AV *arr_object; AV *arr_trait; HV *hv_string; HV *hv_object; HV *hv_trait; int rc_string; int rc_object; int rc_trait; int version; int final_version; int options; struct io_amf_option* ext_option; SV * (*parse_one_object)(pTHX_ struct io_struct * io); char *subname; char status; bool reuse; }; inline SV* get_tmp_storage( pTHX_ SV *option ); inline void destroy_tmp_storage( pTHX_ SV *self); STATIC_INLINE SV * amf0_parse_one(pTHX_ struct io_struct * io); STATIC_INLINE SV * amf3_parse_one(pTHX_ struct io_struct * io); inline void io_in_destroy(pTHX_ struct io_struct * io, AV *); inline void io_out_cleanup(pTHX_ struct io_struct * io); inline void io_test_eof(pTHX_ struct io_struct *io); inline void io_register_error(struct io_struct *io, int); inline void io_register_error_and_free(pTHX_ struct io_struct *io, int, void *); inline int io_position(struct io_struct *io){ return io->pos-io->ptr; } inline void io_set_position(struct io_struct *io, int pos){ io->pos = io->ptr + pos; } inline void io_savepoint(pTHX_ struct io_struct *io, struct amf3_restore_point *p){ p->offset_buffer = io_position(io); p->offset_object = av_len(io->arr_object); p->offset_trait = av_len(io->arr_trait); p->offset_string = av_len(io->arr_string); } inline void io_restorepoint(pTHX_ struct io_struct *io, struct amf3_restore_point *p){ io_set_position(io, p->offset_buffer); while(av_len(io->arr_object) > p->offset_object){ SV * abc = av_pop(io->arr_object); sv_2mortal(abc); } while(av_len(io->arr_trait) > p->offset_trait){ sv_2mortal(av_pop(io->arr_trait)); } while(av_len(io->arr_string) > p->offset_string){ sv_2mortal(av_pop(io->arr_string)); } } inline void io_move_backward(struct io_struct *io, int step){ io->pos-= step; } inline void io_move_forward(struct io_struct *io, int len){ io->pos+=len; } inline void io_require(struct io_struct *io, int len){ if (io->end - io->pos < len){ io_register_error(io, ERR_EOF); } } inline void io_reserve(pTHX_ struct io_struct *io, int len){ if (io->end - io->pos< len){ unsigned int ipos = io->pos - io->ptr; unsigned int buf_len; SvCUR_set(io->sv_buffer, ipos); buf_len = SvLEN(io->sv_buffer); while( buf_len < ipos + len + io->buffer_step_inc){ buf_len *= 4; } io->ptr = (unsigned char *) SvGROW(io->sv_buffer, buf_len); io->pos = io->ptr + ipos; io->end = io->ptr + SvLEN(io->sv_buffer); } } inline void io_register_error(struct io_struct *io, int errtype){ io->error_code = errtype; Siglongjmp(io->target_error, errtype); } inline void io_test_eof(pTHX_ struct io_struct *io){ if (io->pos!=io->end){ io_register_error( io, ERR_EOF ); } } void io_format_error(pTHX_ struct io_struct *io ){ int error_code = io->error_code; char *message; if ( error_code < 1 || error_code >= ARRAY_SIZE( error_messages )){ error_code = ERR_INTERNAL; }; message = error_messages[ error_code - 1]; if ( io->status == 'r' ){ io_in_destroy(aTHX_ io, 0); /* all objects */ if (io->options & OPT_RAISE_ERROR){ croak("Parse AMF%d: %s (ERR-%d)", io->version, message, error_code); } else { sv_setiv(ERRSV, error_code); sv_setpvf(ERRSV, "Parse AMF%d: %s (ERR-%d)", io->version, message, error_code); SvIOK_on(ERRSV); } } else { /* io->status == 'w' */ io_out_cleanup(aTHX_ io); if (io->options & OPT_RAISE_ERROR){ croak("Format AMF%d: %s (ERR-%d)", io->version, message, error_code); } else { sv_setiv(ERRSV, error_code); sv_setpvf(ERRSV, "Format AMF%d: %s (ERR-%d)", io->version, message, error_code); SvIOK_on(ERRSV); } } } inline void io_register_error_and_free(pTHX_ struct io_struct *io, int errtype, void *pointer){ if (pointer) sv_2mortal((SV*) pointer); Siglongjmp(io->target_error, errtype); } inline SV* get_tmp_storage(pTHX_ SV* option){ struct io_struct * io; SV *sv ; AV *tmp; Newxz( io, 1, struct io_struct ); sv = sv_newmortal(); sv_setref_iv( sv, "Storable::AMF0::TemporaryStorage", PTR2IV( io ) ); tmp = io->arr_trait = newAV(); tmp = io->arr_string = newAV(); tmp = io->arr_object = newAV(); io->hv_object = newHV(); HvSHAREKEYS_off( io->hv_object ); io->hv_string = newHV(); HvSHAREKEYS_off( io->hv_string ); io->hv_trait = newHV(); HvSHAREKEYS_off( io->hv_trait ); /* io->sv_buffer = newSV(0); (void)SvUPGRADE(io->sv_buffer, SVt_PV); SvPOK_on( io->sv_buffer ); SvGROW( io->sv_buffer, 512 ); */ if ( option ){ io->options = SvIV(option); } else { io->options = DEFAULT_MASK; } return sv; } inline void destroy_tmp_storage( pTHX_ SV *self ){ if ( ! SvROK( self )) { croak( "Bad Storable::AMF0::TemporaryStorage" ); } else { struct io_struct *io; io = INT2PTR( struct io_struct*, SvIV( SvRV( self )) ); SvREFCNT_dec( (SV *) io->arr_trait ); SvREFCNT_dec( (SV *) io->arr_string ); SvREFCNT_dec( (SV *) io->arr_object ); SvREFCNT_dec( (SV *) io->hv_object ); SvREFCNT_dec( (SV *) io->hv_string ); SvREFCNT_dec( (SV *) io->hv_trait ); // SvREFCNT_dec( (SV *) io->sv_buffer ); Safefree( io ); /* fprintf( stderr, "Destroy\n"); */ } } inline void io_in_cleanup(pTHX_ struct io_struct *io){ av_clear( io->arr_object ); if ( AMF3_VERSION == io->final_version ){ av_clear( io->arr_string ); av_clear( io->arr_trait ); }; } inline void io_out_cleanup(pTHX_ struct io_struct *io){ hv_clear( io->hv_object ); if ( AMF3_VERSION == io->version ){ hv_clear( io->hv_string ); hv_clear( io->hv_trait ); }; } inline void io_in_init(pTHX_ struct io_struct * io, SV* data, int amf_version, SV * sv_option){ struct io_struct *reuse_storage_ptr; bool reuse_storage; /* PerlInterpreter *my_perl = io->interpreter; */ if ( sv_option ){ if (! SvIOK(sv_option)){ if ( ! sv_isobject( sv_option )){ warn( "options are not integer" ); io_register_error( io, ERR_BAD_OPTION ); return; } else { reuse_storage = 1; reuse_storage_ptr = INT2PTR( struct io_struct *, SvIV( SvRV( sv_option ))); io->options = reuse_storage_ptr->options; } } else { reuse_storage = 0; io->options = SvIV(sv_option); } } else { io->options = DEFAULT_MASK; reuse_storage = 0; } io->reuse = reuse_storage; if (SvMAGICAL(data)) mg_get(data); if (!SvPOKp(data)) croak("%s. data must be a string", io->subname); if (SvUTF8(data)) croak("%s: data is utf8. Can't process utf8", io->subname); io->ptr = (unsigned char *) SvPVX(data); io->end = io->ptr + SvCUR(data); io->pos = io->ptr; io->status = 'r'; io->version = amf_version; if ( amf_version == AMF0_VERSION && (io->ptr[0] == MARKER0_AMF_PLUS) ){ amf_version = AMF3_VERSION; ++io->pos; }; io->final_version = amf_version; /* Support when array extend is too big */ io->arr_max = SvCUR( data ); if ( reuse_storage ){ io->arr_object = reuse_storage_ptr->arr_object; if (amf_version == AMF3_VERSION) { io->arr_string = reuse_storage_ptr->arr_string; io->arr_trait = reuse_storage_ptr->arr_trait; io->parse_one_object = amf3_parse_one; } else { io->parse_one_object = amf0_parse_one; } } else { sv_2mortal( (SV *) (io->arr_object = newAV()) ); av_extend( io->arr_object, 32 ); if (amf_version == AMF3_VERSION) { io->arr_string = newAV(); sv_2mortal((SV*) io->arr_string); io->arr_trait = newAV(); sv_2mortal((SV*) io->arr_trait); io->parse_one_object = amf3_parse_one; } else { io->parse_one_object = amf0_parse_one; } } } inline void io_in_destroy(pTHX_ struct io_struct * io, AV *a){ int i; SV **ref_item; int alen; SV *item; if (a) { alen = av_len(a); for(i = 0; i<= alen; ++i){ ref_item = av_fetch(a,i,0); if (ref_item){ if (SvROK(*ref_item)){ item = SvRV(*ref_item); if (SvTYPE(item) == SVt_PVAV){ av_clear((AV*) item); } else if (SvTYPE(item) == SVt_PVHV){ HV * h = (HV*) item; hv_clear(h); } } } } av_clear(a); /* cleaning array */ } else { if (io->final_version == AMF0_VERSION){ io_in_destroy(aTHX_ io, io->arr_object); } else if (io->final_version == AMF3_VERSION) { io_in_destroy(aTHX_ io, io->arr_object); io_in_destroy(aTHX_ io, io->arr_trait); /* May be not needed */ io_in_destroy(aTHX_ io, io->arr_string); } else { croak("bad version at destroy"); } } } inline void io_out_init(pTHX_ struct io_struct *io, SV*sv_option, int amf_version){ SV *sbuffer; unsigned int ibuf_size ; unsigned int ibuf_step ; io->version = amf_version; if ( sv_option ){ if ( SvROK(sv_option) && sv_isobject( sv_option )){ struct io_struct *reuse_storage_ptr; reuse_storage_ptr = INT2PTR( struct io_struct *, SvIV( SvRV( sv_option ))); io->options = reuse_storage_ptr->options; io->reuse = 1; io->rc_string = 0; io->rc_trait = 0; io->rc_object = 0; /*io->sv_buffer = reuse_storage_ptr->sv_buffer; */ io->hv_string = reuse_storage_ptr->hv_string; io->hv_object = reuse_storage_ptr->hv_object; io->hv_trait = reuse_storage_ptr->hv_trait; ibuf_size = 10240; ibuf_step = 20480; if ( io->options & OPT_TARG ){ dXSTARG; io->sv_buffer = sbuffer = TARG; (void)SvUPGRADE(sbuffer, SVt_PV); SvPOK_on(sbuffer); SvGROW( sbuffer, 512 ); } else { sbuffer = sv_2mortal(newSVpvn("",0)); SvGROW(sbuffer, ibuf_size); io->sv_buffer = sbuffer; } io->buffer_step_inc = 1024; io->ptr = (unsigned char *) SvPV_nolen(io->sv_buffer); io->pos = io->ptr; io->end = (unsigned char *) SvEND(io->sv_buffer); io->status = 'w'; return ; } else if ( !SvIOK(sv_option)){ io_register_error( io, ERR_BAD_OPTION ); } else { io->options = SvIV( sv_option ); } } else { io->options = DEFAULT_MASK; }; io->reuse = 0; /* FIXME */ ibuf_size = 10240; ibuf_step = 20480; if ( io->options & OPT_TARG ){ dXSTARG; io->sv_buffer = sbuffer = TARG; (void)SvUPGRADE(sbuffer, SVt_PV); SvPOK_on(sbuffer); SvGROW( sbuffer, 512 ); } else { sbuffer = sv_2mortal(newSVpvn("",0)); SvGROW(sbuffer, ibuf_size); io->sv_buffer = sbuffer; } if (amf_version) { io->hv_string = newHV(); io->hv_trait = newHV(); io->hv_object = newHV(); io->rc_string = 0; io->rc_trait = 0; io->rc_object = 0; HvSHAREKEYS_off( io->hv_object ); HvSHAREKEYS_off( io->hv_trait ); HvSHAREKEYS_off( io->hv_string ); sv_2mortal((SV *)io->hv_string); sv_2mortal((SV *)io->hv_object); sv_2mortal((SV *)io->hv_trait); } else { io->hv_object = newHV(); io->rc_object = 0; HvSHAREKEYS_off( io->hv_object ); sv_2mortal((SV*)io->hv_object); } io->buffer_step_inc = ibuf_step; io->ptr = (unsigned char *) SvPV_nolen(io->sv_buffer); io->pos = io->ptr; io->end = (unsigned char *) SvEND(io->sv_buffer); io->status = 'w'; } inline SV * io_buffer(struct io_struct *io){ SvCUR_set(io->sv_buffer, io->pos - io->ptr); return io->sv_buffer; } inline double io_read_double(struct io_struct *io); inline unsigned char io_read_marker(struct io_struct * io); inline int io_read_u8(struct io_struct * io); inline int io_read_u16(struct io_struct * io); inline int io_read_u32(struct io_struct * io); inline int io_read_u24(struct io_struct * io); #define MOVERFLOW(VALUE, MAXVALUE, PROC)\ if (VALUE > MAXVALUE) { \ PerlIO_printf( PerlIO_stderr(), "Overflow in %s. expected less %d. got %d\n", PROC, MAXVALUE, VALUE); \ io_register_error(io, ERR_OVERFLOW); \ } inline void io_write_double(pTHX_ struct io_struct *io, double value){ const int step = 8; union { signed int iv; unsigned int uv; double nv; char c[8]; } v; io_reserve(aTHX_ io, step ); v.nv = value; io->pos[0] = v.c[GET_NBYTE(step, 0, value)]; io->pos[1] = v.c[GET_NBYTE(step, 1, value)]; io->pos[2] = v.c[GET_NBYTE(step, 2, value)]; io->pos[3] = v.c[GET_NBYTE(step, 3, value)]; io->pos[4] = v.c[GET_NBYTE(step, 4, value)]; io->pos[5] = v.c[GET_NBYTE(step, 5, value)]; io->pos[6] = v.c[GET_NBYTE(step, 6, value)]; io->pos[7] = v.c[GET_NBYTE(step, 7, value)]; io->pos+= step ; return; } inline void io_write_marker(pTHX_ struct io_struct * io, char value) { const int step = 1; io_reserve(aTHX_ io, 1); io->pos[0]= value; io->pos+=step; return; } inline void io_write_uchar (pTHX_ struct io_struct * io, unsigned char value) { const int step = 1; io_reserve(aTHX_ io, 1); io->pos[0]= value; io->pos+=step; return; } inline void io_write_u8(pTHX_ struct io_struct * io, unsigned int value){ const int step = 1; union { signed int iv; unsigned int uv; double nv; char c[8]; } v; v.uv = value; MOVERFLOW(value, 255, "write_u8"); io_reserve(aTHX_ io, 1); io->pos[0]= v.c[GET_NBYTE(step, 0,value)]; io->pos+=step ; return; } inline void io_write_s16(pTHX_ struct io_struct * io, signed int value){ const int step = 2; union { signed int iv; unsigned int uv; double nv; char c[8]; } v; v.iv = value; MOVERFLOW(value, 32767, "write_s16"); io_reserve(aTHX_ io, step); io->pos[0]= v.c[GET_NBYTE(step, 0, value)]; io->pos[1]= v.c[GET_NBYTE(step, 1, value)]; io->pos+=step; return; } inline void io_write_u16(pTHX_ struct io_struct * io, unsigned int value){ const int step = 2; union { signed int iv; unsigned int uv; double nv; char c[8]; } v; io_reserve(aTHX_ io,step); MOVERFLOW(value, 65535 , "write_u16"); v.uv = value; io->pos[0] = v.c[GET_NBYTE(step, 0, value)]; io->pos[1] = v.c[GET_NBYTE(step, 1, value)]; io->pos+=step; return; } inline void io_write_u32(pTHX_ struct io_struct * io, unsigned int value){ const int step = 4; union { signed int iv; unsigned int uv; double nv; char c[8]; } v; io_reserve(aTHX_ io,step); v.uv = value; io->pos[0] = v.c[GET_NBYTE(step, 0, value)]; io->pos[1] = v.c[GET_NBYTE(step, 1, value)]; io->pos[2] = v.c[GET_NBYTE(step, 2, value)]; io->pos[3] = v.c[GET_NBYTE(step, 3, value)]; io->pos+=step; return; } inline void io_write_u24(pTHX_ struct io_struct * io, unsigned int value){ const int step = 3; union { signed int iv; unsigned int uv; double nv; char c[8]; } v; io_reserve(aTHX_ io,step); MOVERFLOW(value,16777215 , "write_u16"); v.uv = value; io->pos[0] = v.c[GET_NBYTE(step, 0, value)]; io->pos[1] = v.c[GET_NBYTE(step, 1, value)]; io->pos[2] = v.c[GET_NBYTE(step, 2, value)]; io->pos+=step; return; } inline void io_write_bytes(pTHX_ struct io_struct* io, const char * const buffer, int len){ io_reserve(aTHX_ io, len); Copy(buffer, io->pos, len, char); io->pos+=len; } /* Date checking */ inline bool util_is_date(SV *one); inline double util_date_time(SV *one); inline void amf0_format_one(pTHX_ struct io_struct *io, SV * one); inline void amf0_format_number(pTHX_ struct io_struct *io, SV * one); inline void amf0_format_string(pTHX_ struct io_struct *io, SV * one); inline void amf0_format_strict_array(pTHX_ struct io_struct *io, AV * one); inline void amf0_format_object(pTHX_ struct io_struct *io, HV * one); inline void amf0_format_null(pTHX_ struct io_struct *io); inline void amf0_format_typed_object(pTHX_ struct io_struct *io, HV * one); inline bool util_is_date(SV *one){ if (SvNOKp(one)){ HV* stash = SvSTASH(one); char *class_name = HvNAME(stash); if (*class_name == '*' && class_name[1] == 0){ return 1; } else { return 0; } } else { return 0; } } inline double util_date_time(SV *one){ return (SvNVX(one)*1000); } inline void amf0_format_reference(pTHX_ struct io_struct * io, SV *ref){ io_write_marker(aTHX_ io, MARKER0_REFERENCE); io_write_u16(aTHX_ io, SvIV(ref)); } inline void amf0_format_scalar_ref(pTHX_ struct io_struct * io, SV *ref){ const char *const reftype = "REFVAL"; io_write_marker(aTHX_ io, MARKER0_TYPED_OBJECT); /* special type */ io_write_u16(aTHX_ io, 6); io_write_bytes(aTHX_ io, reftype, 6); /* type */ io_write_u16(aTHX_ io, 6); io_write_bytes(aTHX_ io, reftype, 6); amf0_format_one(aTHX_ io, ref); /* end marker */ io_write_u16(aTHX_ io, 0); io_write_marker(aTHX_ io, MARKER0_OBJECT_END); } inline void amf0_format_one(pTHX_ struct io_struct *io, SV * one){ if (SvROK(one)){ if ( sv_isobject( one )){ bool is_perl_bool = 0; if ( sv_isa(one, "boolean" )){ is_perl_bool = 1; } if ( sv_isa(one, "JSON::XS::Boolean")){ is_perl_bool = 1; } if ( is_perl_bool ){ io_write_marker(aTHX_ io, MARKER0_BOOLEAN ); /* TODO SvTRUE can call die or something like */ io_write_uchar(aTHX_ io, SvTRUE( SvRV( one )) ? 1 : 0); return ; } } } if (SvROK(one)){ SV * rv = (SV*) SvRV(one); /* test has stored */ SV **OK = hv_fetch(io->hv_object, (char *)(&rv), sizeof (rv), 1); if (SvOK(*OK)) { amf0_format_reference(aTHX_ io, *OK); } else { int type = SvTYPE(rv); sv_setiv(*OK, io->rc_object); ++io->rc_object; if (sv_isobject(one)) { if ( io->options & OPT_MAPPER ){ GV *to_amf = gv_fetchmethod_autoload (SvSTASH (rv), "TO_AMF", 0); if ( to_amf ) { dSP; ENTER; SAVETMPS; PUSHMARK (SP); XPUSHs (sv_bless (sv_2mortal (newRV_inc (rv)), SvSTASH (rv))); /* calling with G_SCALAR ensures that we always get a 1 return value */ PUTBACK; call_sv ((SV *)GvCV (to_amf), G_SCALAR); SPAGAIN; /* catch this surprisingly common error */ if (SvROK (TOPs) && SvRV (TOPs) == rv) croak ("%s::TO_AMF method returned same object as was passed instead of a new one", HvNAME (SvSTASH (rv))); rv = POPs; PUTBACK; amf0_format_one( aTHX_ io, rv); FREETMPS; LEAVE; return ; } } if (SvTYPE(rv) == SVt_PVHV){ amf0_format_typed_object(aTHX_ io, (HV *) rv); } else if ( util_is_date( rv ) ) { io_write_marker(aTHX_ io, MARKER0_DATE ); io_write_double(aTHX_ io, util_date_time( rv )); io_write_s16(aTHX_ io, 0 ); } else { /* may be i has to format as undef */ io_register_error(io, ERR_BAD_OBJECT); } } else if (SvTYPE(rv) == SVt_PVAV) amf0_format_strict_array(aTHX_ io, (AV*) rv); else if (SvTYPE(rv) == SVt_PVHV) { io_write_marker(aTHX_ io, MARKER0_OBJECT); amf0_format_object(aTHX_ io, (HV*) rv); } else if ( type != SVt_PVCV && type != SVt_PVGV ) { amf0_format_scalar_ref(aTHX_ io, (SV*) rv); } else { io_register_error(io, ERR_BAD_OBJECT); } } } else { if (SvOK(one)){ #if defined( EXPERIMENT1 ) if ( (io->options & OPT_PREFER_NUMBER )){ if (SvNIOK(one)){ amf0_format_number(aTHX_ io, one); } else { amf0_format_string(aTHX_ io, one); } } else #endif if (SvPOK(one)){ amf0_format_string(aTHX_ io, one); } else { amf0_format_number(aTHX_ io, one); } } else { amf0_format_null(aTHX_ io); } } } inline void amf0_format_number(pTHX_ struct io_struct *io, SV * one){ io_write_marker(aTHX_ io, MARKER0_NUMBER); io_write_double(aTHX_ io, SvNV(one)); } inline void amf0_format_string(pTHX_ struct io_struct *io, SV * one){ /* TODO: process long string */ if (SvPOK(one)){ STRLEN str_len; char * pv; pv = SvPV(one, str_len); if (str_len > 65500){ io_write_marker(aTHX_ io, MARKER0_LONG_STRING); io_write_u32(aTHX_ io, str_len); io_write_bytes(aTHX_ io, pv, str_len); } else { io_write_marker(aTHX_ io, MARKER0_STRING); io_write_u16(aTHX_ io, SvCUR(one)); io_write_bytes(aTHX_ io, SvPV_nolen(one), SvCUR(one)); } }else{ amf0_format_null(aTHX_ io); } } inline void amf0_format_strict_array(pTHX_ struct io_struct *io, AV * one){ int i, len; AV * one_array; one_array = one; len = av_len(one_array); io_write_marker(aTHX_ io, '\012'); io_write_u32(aTHX_ io, len + 1); for(i = 0; i <= len; ++i){ SV ** ref_value = av_fetch(one_array, i, 0); if (ref_value) { amf0_format_one(aTHX_ io, *ref_value); } else { amf0_format_null(aTHX_ io); } } } inline void amf0_format_object(pTHX_ struct io_struct *io, HV * one){ STRLEN key_len; HV *hv; HE *he; SV * value; char * key_str; hv = one; if (1) { hv_iterinit(hv); while( (he = hv_iternext(hv))){ key_str = HePV(he, key_len); value = HeVAL(he); io_write_u16(aTHX_ io, key_len); io_write_bytes(aTHX_ io, key_str, key_len); amf0_format_one(aTHX_ io, value); } } io_write_u16(aTHX_ io, 0); io_write_marker(aTHX_ io, MARKER0_OBJECT_END); } inline void amf0_format_null(pTHX_ struct io_struct *io){ io_write_marker(aTHX_ io, MARKER0_UNDEFINED); } inline void amf0_format_typed_object(pTHX_ struct io_struct *io, HV * one){ HV* stash = SvSTASH(one); char *class_name = HvNAME(stash); io_write_marker(aTHX_ io, MARKER0_TYPED_OBJECT); io_write_u16(aTHX_ io, (U16) strlen(class_name)); io_write_bytes(aTHX_ io, class_name, strlen(class_name)); amf0_format_object(aTHX_ io, one); } STATIC_INLINE SV * amf0_parse_one(pTHX_ struct io_struct * io); STATIC_INLINE SV* amf0_parse_boolean(pTHX_ struct io_struct *io); STATIC_INLINE SV* amf0_parse_object(pTHX_ struct io_struct *io); STATIC_INLINE SV* amf0_parse_movieclip(pTHX_ struct io_struct *io); STATIC_INLINE SV* amf0_parse_null(pTHX_ struct io_struct *io); STATIC_INLINE SV* amf0_parse_undefined(pTHX_ struct io_struct *io); STATIC_INLINE SV* amf0_parse_reference(pTHX_ struct io_struct *io); STATIC_INLINE SV* amf0_parse_object_end(pTHX_ struct io_struct *io); STATIC_INLINE SV* amf0_parse_strict_array(pTHX_ struct io_struct *io); STATIC_INLINE SV* amf0_parse_ecma_array(pTHX_ struct io_struct *io); STATIC_INLINE SV* amf0_parse_date(pTHX_ struct io_struct *io); STATIC_INLINE SV* amf0_parse_long_string(pTHX_ struct io_struct *io); STATIC_INLINE SV* amf0_parse_unsupported(pTHX_ struct io_struct *io); STATIC_INLINE SV* amf0_parse_recordset(pTHX_ struct io_struct *io); STATIC_INLINE SV* amf0_parse_xml_document(pTHX_ struct io_struct *io); STATIC_INLINE SV* amf0_parse_typed_object(pTHX_ struct io_struct *io); inline void io_write_double(pTHX_ struct io_struct *io, double value); inline void io_write_marker(pTHX_ struct io_struct * io, char value); inline void io_write_uchar (pTHX_ struct io_struct * io, unsigned char value); inline void io_write_u8(pTHX_ struct io_struct * io, unsigned int value); inline void io_write_s16(pTHX_ struct io_struct * io, signed int value); inline void io_write_u16(pTHX_ struct io_struct * io, unsigned int value); inline void io_write_u32(pTHX_ struct io_struct * io, unsigned int value); inline void io_write_u24(pTHX_ struct io_struct * io, unsigned int value); inline double io_read_double(struct io_struct *io){ const int step = sizeof(double); double a; unsigned char * ptr_in = io->pos; char * ptr_out = (char *) &a; io_require(io, step); ptr_out[GET_NBYTE(step, 0, a)] = ptr_in[0] ; ptr_out[GET_NBYTE(step, 1, a)] = ptr_in[1] ; ptr_out[GET_NBYTE(step, 2, a)] = ptr_in[2] ; ptr_out[GET_NBYTE(step, 3, a)] = ptr_in[3] ; ptr_out[GET_NBYTE(step, 4, a)] = ptr_in[4] ; ptr_out[GET_NBYTE(step, 5, a)] = ptr_in[5] ; ptr_out[GET_NBYTE(step, 6, a)] = ptr_in[6] ; ptr_out[GET_NBYTE(step, 7, a)] = ptr_in[7] ; io->pos += step; return a; } inline char *io_read_bytes(struct io_struct *io, int len){ char * pos = ( char * )io->pos; io_require(io, len); io->pos+=len; return pos; } inline char *io_read_chars(struct io_struct *io, int len){ char * pos = ( char * )io->pos; io_require(io, len); io->pos+=len; return pos; } inline unsigned char io_read_marker(struct io_struct * io){ const int step = 1; unsigned char marker; io_require(io, step); marker = *(io->pos); io->pos++; return marker; } inline int io_read_u8(struct io_struct * io){ const int step = 1; union{ unsigned int x; unsigned char bytes[8]; } str; io_require(io, step); str.x = 0; str.bytes[GET_NBYTE(step, 0, str.x)] = io->pos[0]; io->pos+= step; return (int) str.x; } inline int io_read_s16(struct io_struct * io){ const int step = 2; union{ int x; char bytes[8]; } str; io_require(io, step); str.x = io->pos[step - 1] & '\x80' ? -1 : 0; str.bytes[GET_NBYTE(step, 0, str.x)] = io->pos[0]; str.bytes[GET_NBYTE(step, 1, str.x)] = io->pos[1]; io->pos+= step; return (int) str.x; } inline int io_read_u16(struct io_struct * io){ const int step = 2; union{ unsigned int x; char bytes[8]; } str; io_require(io, step); str.x = 0; str.bytes[GET_NBYTE(step, 0, str.x)] = io->pos[0]; str.bytes[GET_NBYTE(step, 1, str.x)] = io->pos[1]; io->pos+= step; return (int) str.x; } inline int io_read_u24(struct io_struct * io){ const int step = 3; union{ unsigned int x; char bytes[8]; } str; io_require(io, step); str.x = 0; str.bytes[GET_NBYTE(step, 0, str.x)] = io->pos[0]; str.bytes[GET_NBYTE(step, 1, str.x)] = io->pos[1]; str.bytes[GET_NBYTE(step, 2, str.x)] = io->pos[2]; io->pos+= step; return (int) str.x; } inline int io_read_u32(struct io_struct * io){ const int step = 4; union{ unsigned int x; char bytes[8]; } str; io_require(io, step); str.x = 0; str.bytes[GET_NBYTE(step, 0, str.x)] = io->pos[0]; str.bytes[GET_NBYTE(step, 1, str.x)] = io->pos[1]; str.bytes[GET_NBYTE(step, 2, str.x)] = io->pos[2]; str.bytes[GET_NBYTE(step, 3, str.x)] = io->pos[3]; io->pos+= step; return (int) str.x; } inline void amf3_write_integer(pTHX_ struct io_struct *io, IV ivalue){ UV value; if (ivalue<0){ if ( ivalue < -( 1 << 28) ){ io_register_error( io, ERR_INT_OVERFLOW ); }; value = 0x1fffffff & (UV) ivalue; } else { value = ivalue; } if (value<128){ io_reserve(aTHX_ io, 1); io->pos[0]= (U8) value; io->pos+=1; } else if (value<= 0x3fff ) { io_reserve(aTHX_ io, 2); io->pos[0] = (U8) (value>>7) | 128; io->pos[1] = (U8) (value & 0x7f); io->pos+=2; } else if (value <= 0x1fffff) { io_reserve(aTHX_ io, 3); io->pos[0] = (U8) (value>>14) | 128; io->pos[1] = (U8) (value>>7 & 0x7f) |128; io->pos[2] = (U8) (value & 0x7f); io->pos+=3; } else if ((value <= 0x1fffffff)){ io_reserve(aTHX_ io, 4); io->pos[0] = (U8) (value>>22 & 0xff) |128; io->pos[1] = (U8) (value>>15 & 0x7f) |128; io->pos[2] = (U8) (value>>8 & 0x7f) |128; io->pos[3] = (U8) (value & 0xff); io->pos+=4; } else { io_register_error( io, ERR_INT_OVERFLOW); return; } return; } inline int amf3_read_integer(struct io_struct *io){ I32 value; io_require(io, 1); if ((U8) io->pos[0] > 0x7f) { io_require(io, 2); if ((U8) io->pos[1] >0x7f) { io_require(io, 3); if ((U8) io->pos[2] >0x7f) { io_require(io, 4); value = ((io->pos[0] & 0x7f) <<22)| ((io->pos[1] & 0x7f) <<15) | ((io->pos[2] & 0x7f) <<8) | io->pos[3]; if ((U8) io->pos[0] >= 0xc0) { value = value | ~(0x0fffffff); } else { /* no return value; */ ; } io_move_forward(io, 4); } else { value = ((io->pos[0] & 0x7f) <<14) + ((io->pos[1] & 0x7f) <<7) + io->pos[2]; io_move_forward(io, 3); } } else { value = ((io->pos[0] & 0x7f) << 7) + io->pos[1]; io_move_forward(io, 2); } } else { value = (U8) io->pos[0]; io_move_forward(io, 1); } return value; } STATIC_INLINE SV * amf0_parse_utf8(pTHX_ struct io_struct * io){ int string_len = io_read_u16(io); SV * RETVALUE; char *x = io_read_chars(io, string_len); RETVALUE = newSVpvn(x, string_len); if (io->options & OPT_DECODE_UTF8) SvUTF8_on(RETVALUE); return RETVALUE; } STATIC_INLINE SV * amf0_parse_object(pTHX_ struct io_struct * io){ HV * obj; int len_next; char * key; SV * value; int obj_pos; SV *RETVALUE; obj = newHV(); RETVALUE = newRV_noinc( (SV *) obj ); av_push(io->arr_object, RETVALUE); obj_pos = av_len(io->arr_object); while(1){ len_next = io_read_u16(io); if (len_next == 0) { char object_end; object_end= io_read_marker(io); if ((object_end == MARKER0_OBJECT_END)) { if (io->options & OPT_STRICT){ if (SvREFCNT(RETVALUE) > 1) io_register_error(io, ERR_RECURRENT_OBJECT); ; SvREFCNT_inc_simple_void_NN(RETVALUE); return RETVALUE; } else { SvREFCNT_inc_simple_void_NN(RETVALUE); return RETVALUE; } } else { io->pos--; key = ""; value = amf0_parse_one(aTHX_ io); } } else { key = io_read_chars(io, len_next); value = amf0_parse_one(aTHX_ io); } (void) hv_store(obj, key, len_next, value, 0); } } STATIC_INLINE SV* amf0_parse_movieclip(pTHX_ struct io_struct *io){ SV* RETVALUE; RETVALUE = newSV(0); return RETVALUE; } STATIC_INLINE SV* amf0_parse_null(pTHX_ struct io_struct *io){ SV* RETVALUE; RETVALUE = newSV(0); return RETVALUE; } STATIC_INLINE SV* amf0_parse_undefined(pTHX_ struct io_struct *io){ SV* RETVALUE; RETVALUE = newSV(0); return RETVALUE; } STATIC_INLINE SV* amf0_parse_reference(pTHX_ struct io_struct *io){ SV* RETVALUE; int object_offset; AV * ar_refs; object_offset = io_read_u16(io); ar_refs = (AV *) io->arr_object; if (object_offset > av_len(ar_refs)){ io_register_error(io, ERR_AMF0_REF); } else { RETVALUE = *av_fetch(ar_refs, object_offset, 0); SvREFCNT_inc_simple_void_NN(RETVALUE); } return RETVALUE; } STATIC_INLINE SV* amf0_parse_object_end(pTHX_ struct io_struct *io){ io_read_marker(io); return 0; } STATIC_INLINE SV* amf0_parse_strict_array(pTHX_ struct io_struct *io){ SV* RETVALUE; int array_len; AV* this_array; AV * refs = io->arr_object; int i; refs = (AV*) io->arr_object; array_len = io_read_u32(io); /* report error before av_extent */ if ( array_len > io->arr_max ) io_register_error( io, ERR_ARRAY_TOO_BIG ); else io->arr_max -= array_len; this_array = newAV(); av_extend(this_array, array_len); av_push(refs, RETVALUE = newRV_noinc((SV*) this_array)); for(i=0; i 1 && io->options & OPT_STRICT) io_register_error(io, ERR_RECURRENT_OBJECT); SvREFCNT_inc_simple_void_NN(RETVALUE); return RETVALUE; } STATIC_INLINE SV* amf0_parse_ecma_array(pTHX_ struct io_struct *io){ SV* RETVALUE; U32 array_len; AV * this_array; AV * refs = io->arr_object; int position; /*remember offset for array convertion to hash */ int last_len; char last_marker; int av_refs_len; int key_len; char *key_ptr; array_len = io_read_u32(io); position= io_position(io); /* report_array early */ if ( array_len > io->arr_max ) io_register_error( io, ERR_ARRAY_TOO_BIG); else io->arr_max -= array_len; this_array = newAV(); av_extend(this_array, array_len); av_refs_len = av_len(refs); av_push(refs, RETVALUE = newRV_noinc((SV*) this_array)); #ifdef TRACEA fprintf( stderr, "Start parse array %d\n", array_len); fprintf( stderr, "position %d\n", io_position(io)); #endif if (0 <= array_len){ bool ok; UV index; key_len = io_read_u16(io); key_ptr = io_read_chars(io, key_len); ok = ((key_len == 1) && (IS_NUMBER_IN_UV & grok_number(key_ptr, key_len, &index)) && (index < array_len )); if (ok){ av_store(this_array, index, amf0_parse_one(aTHX_ io)); } else { if (((key_len) == 6 && strnEQ(key_ptr, "length", 6))){ ok = 1; array_len++; /* safe for flash v.9.0 */ sv_2mortal( amf0_parse_one(aTHX_ io)); } else { ok = 0; }; } if (ok){ U32 i; for(i=1; ioptions & OPT_STRICT && (SvREFCNT(RETVALUE) > 1)) io_register_error(io, ERR_RECURRENT_OBJECT); ; SvREFCNT_inc_simple_void_NN(RETVALUE); } else{ /* Need rollback referenses */ int i; for( i = av_len(refs) - av_refs_len; i>0 ;--i){ SV * ref = av_pop(refs); SV * value= SvRV(ref); if ( SVt_PVHV == SvTYPE( value ) ) hv_clear( (HV *) value ); else if ( SVt_PVAV == SvTYPE( value )) av_clear( (AV *) value); else { /* FIXME I am not sure about simple mortalizing values this need to be reused or cleanups*/ sv_2mortal(ref); io_register_error( io, ERR_INTERNAL ); } sv_2mortal(ref); } io_set_position(io, position); RETVALUE = amf0_parse_object(aTHX_ io); } return RETVALUE; } STATIC_INLINE SV* amf0_parse_date(pTHX_ struct io_struct *io){ SV* RETVALUE; double time; int tz; time = io_read_double(io); tz = io_read_s16(io); if ( io->options & OPT_MILLSEC_DATE ) RETVALUE = newSVnv(time); else RETVALUE = newSVnv(time/1000.0); av_push(io->arr_object, RETVALUE); SvREFCNT_inc_simple_void_NN(RETVALUE); return RETVALUE; } STATIC_INLINE SV* amf0_parse_long_string(pTHX_ struct io_struct *io){ SV* RETVALUE; STRLEN len; len = io_read_u32(io); RETVALUE = newSVpvn(io_read_chars(io, len), len); if (io->options & OPT_DECODE_UTF8) SvUTF8_on(RETVALUE); return RETVALUE; } STATIC_INLINE SV* amf0_parse_unsupported(pTHX_ struct io_struct *io){ io_register_error(io, ERR_UNIMPLEMENTED); return 0; } STATIC_INLINE SV* amf0_parse_recordset(pTHX_ struct io_struct *io){ io_register_error(io, ERR_UNIMPLEMENTED); return 0; } STATIC_INLINE SV* amf0_parse_xml_document(pTHX_ struct io_struct *io){ SV* RETVALUE; RETVALUE = amf0_parse_long_string(aTHX_ io); SvREFCNT_inc_simple_void_NN(RETVALUE); av_push(io->arr_object, RETVALUE); return RETVALUE; } inline SV *parse_scalar_ref(pTHX_ struct io_struct *io){ SV * obj; int obj_pos; int len_next; char *key; SV *value; io->pos+=6; obj = newSV(0); av_push(io->arr_object, obj); obj_pos = av_len(io->arr_object); value = 0; while(1){ len_next = io_read_u16(io); if (len_next == 0) { char object_end; object_end= io_read_marker(io); if ((object_end == MARKER0_OBJECT_END)) { SV* RETVALUE = *av_fetch(io->arr_object, obj_pos, 0); if (!value) io_register_error(io, ERR_BAD_REFVAL); sv_setsv(obj, newRV_noinc(value)); if (io->options & OPT_STRICT){ if (SvREFCNT(RETVALUE) > 1) io_register_error_and_free(aTHX_ io, ERR_RECURRENT_OBJECT, value); ; SvREFCNT_inc_simple_void_NN(RETVALUE); return RETVALUE; } else { SvREFCNT_inc_simple_void_NN(RETVALUE); return RETVALUE; } } else { io_register_error_and_free(aTHX_ io, ERR_BAD_REFVAL, value); } } else if ( len_next == 6) { key = io_read_chars(io, len_next); if (strncmp(key, "REFVAL", 6) || value ) io_register_error_and_free(aTHX_ io, ERR_BAD_REFVAL, value); value = amf0_parse_one(aTHX_ io); } else { io_register_error_and_free(aTHX_ io, ERR_BAD_REFVAL, value); } } } STATIC_INLINE SV* amf0_parse_typed_object(pTHX_ struct io_struct *io){ SV* RETVALUE; HV *stash; int len; len = io_read_u16(io); if (len == 6 && !strncmp( (char *)io->pos, "REFVAL", 6)){ /* SCALAR */ RETVALUE = parse_scalar_ref(aTHX_ io); if (RETVALUE) return RETVALUE; } if (io->options & OPT_STRICT){ stash = gv_stashpvn((char *)io->pos, len, 0); } else { stash = gv_stashpvn((char *)io->pos, len, GV_ADD ); } io->pos+=len; RETVALUE = amf0_parse_object(aTHX_ io); if (stash) sv_bless(RETVALUE, stash); return RETVALUE; } STATIC_INLINE SV* amf0_parse_double(pTHX_ struct io_struct * io){ return newSVnv(io_read_double(io)); } inline SV* util_boolean(pTHX_ struct io_struct *io, bool value){ if ( ! (io->options & OPT_JSON_BOOLEAN) ){ SV *sv = boolSV( value ); /* SvREFCNT_inc_simple_void_NN( sv ); */ return sv; } else { SV * sv = value ? newSViv(1) :newSViv( 0 ); SV * rv = newRV_noinc( sv ); HV * stash; stash = gv_stashpvn( "JSON::XS::Boolean", 17, GV_ADD ); sv_bless( rv, stash ); /*Stupid but it works */ return rv; } } STATIC_INLINE SV* amf0_parse_boolean(pTHX_ struct io_struct * io){ unsigned char marker; bool value; marker = io_read_marker(io); value = (marker != '\000'); return util_boolean(aTHX_ io, value); } /* STATIC_INLINE SV* parse_boolean(pTHX_ struct io_struct * io){ char marker; marker = io_read_marker(io); int count; SV *value = 0; SAVETMPS; dSP; ENTER; SAVETMPS; PUSHMARK (SP); PUTBACK; if ( marker == '\000' ) { count = call_pv("JSON::XS::false", G_SCALAR); } else { count = call_pv("JSON::XS::true", G_SCALAR); } SPAGAIN; if (count == 1) value = newSVsv(POPs); if (count != 1 || !SvOK(value)) { value = newSViv( marker == '\000' ? 0 : 1 ); } PUTBACK; FREETMPS; LEAVE; return value; } */ inline SV * amf3_parse_one(pTHX_ struct io_struct *io); STATIC_INLINE SV * amf3_parse_undefined(pTHX_ struct io_struct *io){ SV * RETVALUE; RETVALUE = newSV(0); return RETVALUE; } STATIC_INLINE SV * amf3_parse_null(pTHX_ struct io_struct *io){ SV * RETVALUE; RETVALUE = newSV(0); return RETVALUE; } STATIC_INLINE SV * amf3_parse_false(pTHX_ struct io_struct *io){ SV * RETVALUE; RETVALUE = util_boolean( aTHX_ io, 0 ); return RETVALUE; } STATIC_INLINE SV * amf3_parse_true(pTHX_ struct io_struct *io){ SV * RETVALUE; RETVALUE = util_boolean( aTHX_ io, 1 ); return RETVALUE; } STATIC_INLINE SV * amf3_parse_integer(pTHX_ struct io_struct *io){ SV * RETVALUE; RETVALUE = newSViv(amf3_read_integer(io)); return RETVALUE; } STATIC_INLINE SV * amf3_parse_double(pTHX_ struct io_struct *io){ SV * RETVALUE; RETVALUE = newSVnv(io_read_double(io)); return RETVALUE; } inline char * amf3_read_string(pTHX_ struct io_struct *io, int ref_len, STRLEN *str_len){ AV * arr_string = io->arr_string; if (ref_len & 1) { *str_len = ref_len >> 1; if (*str_len>0){ char *pstr; pstr = io_read_chars(io, *str_len); av_push(io->arr_string, newSVpvn(pstr, *str_len)); return pstr; } else { return ""; } } else { int ref = ref_len >> 1; SV ** ref_sv = av_fetch(arr_string, ref, 0); if (ref_sv) { char* pstr; pstr = SvPV(*ref_sv, *str_len); return pstr; } else { /* Exception: May be there throw some */ io_register_error(io, ERR_BAD_STRING_REF); return 0; /* Never reach this lime */ } } } STATIC_INLINE SV * amf3_parse_string(pTHX_ struct io_struct *io){ SV * RETVALUE; int ref_len; STRLEN plen; char* pstr; ref_len = amf3_read_integer(io); pstr = amf3_read_string(aTHX_ io, ref_len, &plen); RETVALUE = newSVpvn(pstr, plen); if (io->options & OPT_DECODE_UTF8) SvUTF8_on(RETVALUE); return RETVALUE; } STATIC_INLINE SV * amf3_parse_xml(pTHX_ struct io_struct *io); STATIC_INLINE SV * amf3_parse_xml_doc(pTHX_ struct io_struct *io){ SV * RETVALUE; RETVALUE = amf3_parse_xml(aTHX_ io); return RETVALUE; } inline SV * amf3_parse_date(pTHX_ struct io_struct *io){ SV * RETVALUE; int i = amf3_read_integer(io); if (i&1){ double x = io_read_double(io); if ( io->options & OPT_MILLSEC_DATE ){ RETVALUE = newSVnv(x); } else { RETVALUE = newSVnv(x/1000.0); }; SvREFCNT_inc_simple_void_NN(RETVALUE); av_push(io->arr_object, RETVALUE); } else { SV ** item = av_fetch(io->arr_object, i>>1, 0); if (item) { RETVALUE = *item; SvREFCNT_inc_simple_void_NN(RETVALUE); } else{ io_register_error(io, ERR_BAD_DATE_REF); RETVALUE = 0; /* did not make any harm */ } } return RETVALUE; } inline void amf3_store_object(pTHX_ struct io_struct *io, SV * item){ av_push(io->arr_object, newRV_noinc(item)); } inline void amf3_store_object_rv(pTHX_ struct io_struct *io, SV * item){ av_push(io->arr_object, item); } inline SV * amf3_parse_array(pTHX_ struct io_struct *io){ SV * RETVALUE; int ref_len = amf3_read_integer(io); if (ref_len & 1){ /* Not referense */ int len = ref_len>>1; int str_len; SV * item; char * pstr; bool recover; STRLEN plen; struct amf3_restore_point rec_point; int old_vlen; SV * item_value; UV item_index; int obj_pos; AV * array; str_len = amf3_read_integer(io); old_vlen = str_len; io_savepoint(aTHX_ io, &rec_point); /* Пытаемся востановить как массив Считаем что это массив если первый индекс от 0 до 9 и все индексы числовые */ array=newAV(); item = (SV *) array; RETVALUE = newRV_noinc(item); amf3_store_object_rv(aTHX_ io, RETVALUE); obj_pos = av_len(io->arr_object); recover = FALSE; if (str_len !=1){ pstr = amf3_read_string(aTHX_ io, str_len, &plen); if (IS_NUMBER_IN_UV & grok_number(pstr, plen, &item_index) && item_index< 10){ item_value= amf3_parse_one(aTHX_ io); av_store(array, item_index, item_value); str_len = amf3_read_integer(io); while(str_len != 1){ pstr = amf3_read_string(aTHX_ io, str_len, &plen); if (IS_NUMBER_IN_UV & grok_number(pstr, plen, &item_index)){ item_value= amf3_parse_one(aTHX_ io); av_store(array, item_index, item_value); str_len = amf3_read_integer(io); } else { /* recover */ recover = TRUE; break; } }; } else { /* recover */ recover = TRUE; } } if (!recover) { int i; for(i=0; i< len; ++i){ SV *item = amf3_parse_one(aTHX_ io); av_store(array, i, item); }; } else { /*востанавливаем как хэш */ HV * hv; char *pstr; STRLEN plen; char buf[2+2*sizeof(int)]; int i; io_restorepoint(aTHX_ io, &rec_point); str_len = old_vlen; hv = newHV(); item = (SV *) hv; RETVALUE = newRV_noinc(item); amf3_store_object_rv(aTHX_ io, RETVALUE); while(str_len != 1){ SV *one; pstr = amf3_read_string(aTHX_ io, str_len, &plen); one = amf3_parse_one(aTHX_ io); (void) hv_store(hv, pstr, plen, one, 0); str_len = amf3_read_integer(io); }; for(i=0; ioptions & OPT_STRICT){ if (SvREFCNT(RETVALUE)>1){ io_register_error(io, ERR_RECURRENT_OBJECT); } } SvREFCNT_inc_simple_void_NN(RETVALUE); } else { SV ** value = av_fetch(io->arr_object, ref_len>>1, 0); if (value) { SvREFCNT_inc_simple_void_NN(*value); RETVALUE = *value; } else { io_register_error(io, ERR_BAD_ARRAY_REF); RETVALUE = 0; /* did not make any harm */ } } return RETVALUE; } struct amf3_trait_struct{ int sealed; bool dynamic; bool externalizable; SV* class_name; HV* stash; }; inline SV * amf3_parse_object(pTHX_ struct io_struct *io){ SV * RETVALUE; int obj_ref = amf3_read_integer(io); #ifdef TRACE0 fprintf(stderr, "obj_ref = %d\n", obj_ref); #endif if (obj_ref & 1) {/* not a ref object */ AV * trait; int sealed; bool dynamic; bool externalizable; SV * class_name_sv; HV *one; int i; if (!(obj_ref & 2)){/* not trait ref */ SV** trait_item = av_fetch(io->arr_trait, obj_ref>>2, 0); if (! trait_item) { io_register_error(io, ERR_BAD_TRAIT_REF); }; trait = (AV *) SvRV(*trait_item); sealed = (int) SvIV(*av_fetch(trait, 0, 0)); dynamic = (bool) SvIV(*av_fetch(trait, 1, 0)); externalizable = (bool) SvIV(*av_fetch(trait, 2, 0)); class_name_sv = *av_fetch(trait, 3, 0); } else { int i; trait = newAV(); av_push(io->arr_trait, newRV_noinc((SV *) trait)); sealed = obj_ref >>4; dynamic = obj_ref & 8; externalizable = ( obj_ref & 0x04) != 0; class_name_sv = amf3_parse_string(aTHX_ io); av_push(trait, newSViv(sealed)); av_push(trait, newSViv(dynamic)); av_push(trait, newSViv( externalizable )); /* external processing */ av_push(trait, class_name_sv); for(i =0; i 1 && io->options & OPT_STRICT){ io_register_error(io, ERR_RECURRENT_OBJECT); }; SvREFCNT_inc_simple_void_NN(RETVALUE); if (SvCUR(class_name_sv)) { HV *stash; if (io->options & OPT_STRICT){ stash = gv_stashsv(class_name_sv, 0 ); } else { stash = gv_stashsv(class_name_sv, GV_ADD ); } if (stash) sv_bless(RETVALUE, stash); } else { /* No bless */ } } else { SV ** ref = av_fetch(io->arr_object, obj_ref>>1, 0); if (ref) { RETVALUE = *ref; SvREFCNT_inc_simple_void_NN(RETVALUE); } else { io_register_error(io, ERR_BAD_TRAIT_REF); RETVALUE = &PL_sv_undef; } } return RETVALUE; } STATIC_INLINE SV * amf3_parse_xml(pTHX_ struct io_struct *io){ SV * RETVALUE; int Bi = amf3_read_integer(io); if (Bi & 1) { /* value */ int len = Bi>>1; char *b = io_read_bytes(io, len); RETVALUE = newSVpvn(b, len); if (io->options & OPT_DECODE_UTF8) SvUTF8_on(RETVALUE); SvREFCNT_inc_simple_void_NN(RETVALUE); av_push(io->arr_object, RETVALUE); } else { SV ** sv = av_fetch(io->arr_object, Bi>>1, 0); if (sv) { RETVALUE = newSVsv(*sv); } else { io_register_error(io, ERR_BAD_XML_REF); } } return RETVALUE; } STATIC_INLINE SV * amf3_parse_bytearray(pTHX_ struct io_struct *io){ SV * RETVALUE; int Bi = amf3_read_integer(io); if (Bi & 1) { /* value */ int len = Bi>>1; char *b = io_read_bytes(io, len); RETVALUE = newSVpvn(b, len); SvREFCNT_inc_simple_void_NN(RETVALUE); av_push(io->arr_object, RETVALUE); } else { SV ** sv = av_fetch(io->arr_object, Bi>>1, 0); if (sv) { RETVALUE = newSVsv(*sv); } else { io_register_error(io, ERR_BAD_BYTEARRAY_REF); } } return RETVALUE; } inline void amf3_format_date( pTHX_ struct io_struct *io, SV * one){ io_write_marker( aTHX_ io, MARKER3_DATE ); amf3_write_integer( aTHX_ io, 1 ); io_write_double( aTHX_ io, util_date_time( one )); } inline void amf3_format_one(pTHX_ struct io_struct *io, SV * one); inline void amf3_format_integer(pTHX_ struct io_struct *io, SV *one){ IV i = SvIV(one); if (i <= 0xfffffff && i>= -(0x10000000)){ io_write_marker(aTHX_ io, MARKER3_INTEGER); amf3_write_integer(aTHX_ io, SvIV(one)); } else { io_write_marker(aTHX_ io, MARKER3_DOUBLE); io_write_double(aTHX_ io, (double) i); } } inline void amf3_format_double(pTHX_ struct io_struct * io, SV *one){ io_write_marker(aTHX_ io, MARKER3_DOUBLE); io_write_double(aTHX_ io, SvNV(one)); } inline void amf3_format_undef(pTHX_ struct io_struct *io){ io_write_marker(aTHX_ io, MARKER3_UNDEF); } inline void amf3_format_null(pTHX_ struct io_struct *io){ io_write_marker(aTHX_ io, MARKER3_NULL); } inline void amf3_write_string_pvn(pTHX_ struct io_struct *io, char *pstr, STRLEN plen){ HV* rhv; SV ** hv_item; rhv = io->hv_string; hv_item = hv_fetch(rhv, pstr, plen, 0); if (hv_item && SvOK(*hv_item)){ int sref = SvIV(*hv_item); amf3_write_integer(aTHX_ io, sref <<1); } else { if (plen) { amf3_write_integer(aTHX_ io, (plen << 1) | 1); io_write_bytes(aTHX_ io, pstr, plen); (void) hv_store(rhv, pstr, plen, newSViv(io->rc_string), 0); io->rc_string++; } else { io_write_marker(aTHX_ io, STR_EMPTY); } } } inline void amf3_format_string(pTHX_ struct io_struct *io, SV *one){ char *pstr; STRLEN plen; pstr = SvPV(one, plen); io_write_marker(aTHX_ io, MARKER3_STRING); amf3_write_string_pvn(aTHX_ io, pstr, plen); } inline void amf3_format_reference(pTHX_ struct io_struct *io, SV *num){ amf3_write_integer(aTHX_ io, SvIV(num)<<1); } inline void amf3_format_array(pTHX_ struct io_struct *io, AV * one){ int alen; int i; SV ** aitem; io_write_marker(aTHX_ io, MARKER3_ARRAY); alen = av_len(one)+1; amf3_write_integer(aTHX_ io, 1 | (alen) <<1 ); io_write_marker(aTHX_ io, STR_EMPTY); /* no sparse array; */ for( i = 0; ihv_trait, class_name, class_name_len, 0); if (rv_trait){ int ref_trait; trait = (AV *) SvRV(*rv_trait); ref_trait = SvIV( *av_fetch(trait, 1, 0)); amf3_write_integer(aTHX_ io, (ref_trait<< 2) | 1); } else { SV * class_name_sv; int const sealed_count = 0; trait = newAV(); av_extend(trait, 3); class_name_sv = newSVpvn(class_name, class_name_len); rv_trait = hv_store( io->hv_trait, class_name, class_name_len, newRV_noinc((SV*)trait), 0); av_store(trait, 0, class_name_sv); av_store(trait, 1, newSViv(io->rc_trait)); av_store(trait, 2, newSViv(0)); amf3_write_integer(aTHX_ io, ( sealed_count << 4) | 0x0b ); amf3_write_string_pvn(aTHX_ io, class_name, class_name_len); io->rc_trait++; } /* where must enumeration of sealed attributes where will dynamic properties */ if (1){ HV *hv; SV * value; char * key_str; I32 key_len; hv = one; hv_iterinit(hv); while( (value = hv_iternextsv(hv, &key_str, &key_len)) ){ if (key_len){ amf3_write_string_pvn(aTHX_ io, key_str, key_len); amf3_format_one(aTHX_ io, value); }; } } io_write_marker(aTHX_ io, STR_EMPTY); } inline void amf3_format_one(pTHX_ struct io_struct *io, SV * one){ if (SvROK(one)){ if ( sv_isobject( one )){ bool is_perl_bool = 0; if ( sv_isa(one, "boolean" )){ is_perl_bool = 1; } if ( sv_isa(one, "JSON::XS::Boolean")){ is_perl_bool = 1; } if ( is_perl_bool ){ io_write_marker(aTHX_ io, (SvTRUE( SvRV( one )) ? MARKER3_TRUE : MARKER3_FALSE ) ); return ; } } } if (SvROK(one)){ SV * rv = (SV*) SvRV(one); /* test has stored */ SV **OK = hv_fetch(io->hv_object, (char *)(&rv), sizeof (rv), 1); if (SvOK(*OK)) { if (SvTYPE(rv) == SVt_PVAV) { io_write_marker(aTHX_ io, MARKER3_ARRAY); amf3_format_reference(aTHX_ io, *OK); } else if (SvTYPE(rv) == SVt_PVHV){ io_write_marker(aTHX_ io, MARKER3_OBJECT); amf3_format_reference(aTHX_ io, *OK); } else if (sv_isobject(one) && util_is_date(rv)){ io_write_marker(aTHX_ io, MARKER3_OBJECT ); /*#TODO */ amf3_format_reference(aTHX_ io, *OK); } else { io_register_error(io, ERR_BAD_OBJECT); } } else { sv_setiv(*OK, io->rc_object); (void) hv_store(io->hv_object, (char *) (&rv), sizeof (rv), newSViv(io->rc_object), 0); ++io->rc_object; if ( io->options && OPT_MAPPER ){ if ( sv_isobject( one ) ){ GV *to_amf = gv_fetchmethod_autoload (SvSTASH (rv), "TO_AMF", 0); if ( to_amf ) { dSP; ENTER; SAVETMPS; PUSHMARK (SP); XPUSHs (sv_bless (sv_2mortal (newRV_inc (rv)), SvSTASH (rv))); /* calling with G_SCALAR ensures that we always get a 1 return value */ PUTBACK; call_sv ((SV *)GvCV (to_amf), G_SCALAR); SPAGAIN; /* catch this surprisingly common error */ if (SvROK (TOPs) && SvRV (TOPs) == rv) croak ("%s::TO_AMF method returned same object as was passed instead of a new one", HvNAME (SvSTASH (rv))); rv = POPs; PUTBACK; amf3_format_object( aTHX_ io, rv); FREETMPS; LEAVE; return ; } } } if (SvTYPE(rv) == SVt_PVAV) amf3_format_array(aTHX_ io, (AV*) rv); else if (SvTYPE(rv) == SVt_PVHV) { amf3_format_object(aTHX_ io, one); } else if (sv_isobject( one ) && util_is_date( rv ) ){ amf3_format_date(aTHX_ io, rv ); } else { io_register_error(io, ERR_BAD_OBJECT); } } } else { if (SvOK(one)){ #if defined( EXPERIMENT1 ) if ( (io->options & OPT_PREFER_NUMBER )){ if (SvNIOK(one)){ if ( SvIOK( one ) ){ amf3_format_integer(aTHX_ io, one ); } else { amf3_format_double(aTHX_ io, one); } } else { amf3_format_string(aTHX_ io, one); } } else #endif if (SvPOK(one)) { amf3_format_string(aTHX_ io, one); } else if (SvIOK(one)){ amf3_format_integer(aTHX_ io, one); } else if (SvNOK(one)){ amf3_format_double(aTHX_ io, one); } else { io_register_error(io, ERR_BAD_OBJECT ); } } else { amf3_format_null(aTHX_ io); } } } typedef SV* (*parse_sub)(pTHX_ struct io_struct *io); parse_sub parse_subs[] = { &amf0_parse_double, &amf0_parse_boolean, &amf0_parse_utf8, &amf0_parse_object, &amf0_parse_movieclip, &amf0_parse_null, &amf0_parse_undefined, &amf0_parse_reference, &amf0_parse_ecma_array, &amf0_parse_object_end, &amf0_parse_strict_array, &amf0_parse_date, &amf0_parse_long_string, &amf0_parse_unsupported, &amf0_parse_recordset, &amf0_parse_xml_document, &amf0_parse_typed_object }; parse_sub amf3_parse_subs[] = { &amf3_parse_undefined, &amf3_parse_null, &amf3_parse_false, &amf3_parse_true, &amf3_parse_integer, &amf3_parse_double, &amf3_parse_string, &amf3_parse_xml_doc, &amf3_parse_date, &amf3_parse_array, &amf3_parse_object, &amf3_parse_xml, &amf3_parse_bytearray, }; inline SV * amf3_parse_one(pTHX_ struct io_struct * io){ unsigned char marker; marker = (unsigned char) io_read_marker(io); if (marker < ARRAY_SIZE( amf3_parse_subs )){ return (amf3_parse_subs[marker])(aTHX_ io); } else { io_register_error(io, ERR_MARKER); return 0; /* Never reach this statement */ } } inline SV* amf0_parse_one_tmp( pTHX_ struct io_struct *io, SV * reuse ){ char marker; SV * RETVALUE; HV * obj; int len_next; char * key; SV * value; int obj_pos; io_require( io, 1 ); marker = *(io->pos); RETVALUE = reuse; if ( MARKER0_OBJECT != MARKER0_OBJECT || ! SvROK( reuse ) ){ io_register_error( io, ERR_BAD_OBJECT ); } obj = (HV *) SvRV(reuse); if ( SvTYPE( obj ) != SVt_PVHV ){ io_register_error( io, ERR_BAD_OBJECT ); } ++io->pos; hv_clear( obj ); SvREFCNT_inc_simple_void_NN( RETVALUE ); av_push(io->arr_object, RETVALUE); obj_pos = av_len(io->arr_object); while(1){ len_next = io_read_u16(io); if (len_next == 0) { char object_end; object_end= io_read_marker(io); if ((object_end == MARKER0_OBJECT_END)) { if (io->options & OPT_STRICT){ SV* RETVALUE = *av_fetch(io->arr_object, obj_pos, 0); if (SvREFCNT(RETVALUE) > 1) io_register_error(io, ERR_RECURRENT_OBJECT); ; SvREFCNT_inc_simple_void_NN(RETVALUE); return RETVALUE; } else { SvREFCNT_inc_simple_void_NN( RETVALUE ); return RETVALUE; /* return (SV*) newRV_inc((SV*)obj); */ } } else { io->pos--; key = ""; value = amf0_parse_one(aTHX_ io); } } else { key = io_read_chars(io, len_next); value = amf0_parse_one(aTHX_ io); } (void) hv_store(obj, key, len_next, value, 0); } } STATIC_INLINE SV * amf0_parse_one(pTHX_ struct io_struct * io){ unsigned char marker; marker = (unsigned char) io_read_marker(io); if ( marker < ARRAY_SIZE( parse_subs )){ return (parse_subs[marker])(aTHX_ io); } else { return io_register_error(io, ERR_MARKER),(SV *)0; } } inline SV * deep_clone(pTHX_ SV * value); inline AV * deep_array(pTHX_ AV* value){ AV* copy = (AV*) newAV(); int c_len; int i; av_extend(copy, c_len = av_len(value)); for(i = 0; i <= c_len; ++i){ av_store(copy, i, deep_clone(aTHX_ *av_fetch(value, i, 0))); } return copy; } inline HV * deep_hash(pTHX_ HV* value){ HV * copy = (HV*) newHV(); SV * key_value; char * key_str; I32 key_len; SV* copy_val; hv_iterinit(value); while((key_value = hv_iternextsv(value, &key_str, &key_len)) ){ copy_val = deep_clone(aTHX_ key_value); (void) hv_store(copy, key_str, key_len, copy_val, 0); } return copy; } inline SV * deep_scalar(pTHX_ SV * value){ return deep_clone(aTHX_ value); } inline SV * deep_clone(pTHX_ SV * value){ if (SvROK(value)){ SV * rv = (SV*) SvRV(value); SV * copy; if (SvTYPE(rv) == SVt_PVHV) { copy = newRV_noinc((SV*)deep_hash(aTHX_ (HV*) rv)); } else if (SvTYPE(rv) == SVt_PVAV) { copy = newRV_noinc((SV*)deep_array(aTHX_ (AV*) rv)); } else if (SvROK(rv)) { copy = newRV_noinc((SV*)deep_clone(aTHX_ (SV*) rv)); } else { /* TODO: error checking return newSV(0); */ copy = newRV_noinc(deep_clone(aTHX_ rv)); } if (sv_isobject(value)) { HV * stash; stash = SvSTASH(rv); sv_bless(copy, stash); } return copy; } else { SV * copy; copy = newSV(0); if (SvOK(value)){ sv_setsv(copy, value); } return copy; } } inline void ref_clear(pTHX_ HV * go_once, SV *sv){ SV *ref_addr; if (! SvROK(sv)) return; ref_addr = SvRV(sv); if (hv_exists(go_once, (char *) &ref_addr, sizeof (ref_addr))) return; (void) hv_store( go_once, (char *) &ref_addr, sizeof(ref_addr), &PL_sv_undef, 0); if (SvTYPE(ref_addr) == SVt_PVAV){ AV * refarray = (AV*) ref_addr; int ref_len = av_len(refarray); int ref_index; for( ref_index = 0; ref_index <= ref_len; ++ref_index){ SV ** ref_item = av_fetch( refarray, ref_index, 0); if (ref_item) ref_clear(aTHX_ go_once, *ref_item); } av_clear(refarray); } else if (SvTYPE(ref_addr) == SVt_PVHV){ HV *ref_hash = (HV *) ref_addr; char * key; I32 key_len; SV* item; hv_iterinit(ref_hash); while( (item = hv_iternextsv(ref_hash, &key, &key_len)) ){ ref_clear(aTHX_ go_once, item); }; hv_clear(ref_hash); } } /* Start XS defines * * * * * */ /* Temporary Intenale Storage */ MODULE = Storable::AMF0 PACKAGE = Storable::AMF0::TemporaryStorage void new(SV *class, SV *option=0) PPCODE: PERL_UNUSED_VAR( class ); XPUSHs( sv_2mortal( get_tmp_storage( aTHX_ option ))); void DESTROY(SV *self) PPCODE: destroy_tmp_storage( aTHX_ self ); MODULE = Storable::AMF0 PACKAGE = Storable::AMF0 void dclone(SV * data) ALIAS: Storable::AMF::dclone= 1 Storable::AMF3::dclone= 2 PROTOTYPE: $ INIT: SV* retvalue; PPCODE: PERL_UNUSED_VAR(ix); retvalue = deep_clone(aTHX_ data); sv_2mortal(retvalue); XPUSHs(retvalue); void amf_tmp_storage(SV *option = 0) INIT: SV * retvalue; PPCODE: retvalue = get_tmp_storage(aTHX_ option); XPUSHs(retvalue); void thaw(SV *data, SV *sv_option = 0) ALIAS: Storable::AMF::thaw=1 Storable::AMF::thaw0=2 PROTOTYPE: $;$ INIT: SV* retvalue; struct io_struct io[1]; PPCODE: PERL_UNUSED_VAR(ix); if ( ! Sigsetjmp(io->target_error, 0) ){ io->subname = "Storable::AMF0::thaw( data, option )"; io_in_init(aTHX_ io, data, AMF0_VERSION, sv_option); retvalue = (SV*) (io->parse_one_object(aTHX_ io)); /* clean up storable unless need */ if ( io->reuse ) io_in_cleanup(aTHX_ io); retvalue = sv_2mortal(retvalue); io_test_eof( aTHX_ io ); sv_setsv(ERRSV, &PL_sv_undef); XPUSHs(retvalue); } else { io_format_error( aTHX_ io ); } void deparse_amf(SV *data, SV * sv_option = 0) PROTOTYPE: $;$ ALIAS: Storable::AMF::deparse_amf=1 Storable::AMF::deparse_amf0=1 INIT: SV* retvalue; struct io_struct io[1]; PPCODE: PERL_UNUSED_VAR(ix); if ( ! Sigsetjmp(io->target_error, 0)){ io->subname = "Storable::AMF0::deparse( data, option )"; io_in_init(aTHX_ io, data, AMF0_VERSION, sv_option); retvalue = (SV*) (io->parse_one_object(aTHX_ io)); /* clean up storable unless need */ if ( io->reuse ) io_in_cleanup(aTHX_ io); retvalue = sv_2mortal(retvalue); sv_setsv(ERRSV, &PL_sv_undef); if (GIMME_V == G_ARRAY){ XPUSHs(retvalue); XPUSHs( sv_2mortal(newSViv( io->pos - io->ptr )) ); } else { XPUSHs(retvalue); } } else { io_format_error( aTHX_ io ); } void freeze(SV *data, SV *sv_option = 0 ) ALIAS: Storable::AMF::freeze=1 Storable::AMF::freeze0=2 PROTOTYPE: $;$ INIT: SV * retvalue; struct io_struct io[1]; PPCODE: PERL_UNUSED_VAR(ix); if (! Sigsetjmp(io->target_error, 0)){ io_out_init(aTHX_ io, sv_option, AMF0_VERSION); amf0_format_one(aTHX_ io, data); if (io->reuse ) io_out_cleanup(aTHX_ io); retvalue = io_buffer(io); XPUSHs(retvalue); sv_setsv(ERRSV, &PL_sv_undef); } else{ io_format_error( aTHX_ io ); } MODULE = Storable::AMF0 PACKAGE = Storable::AMF3 void deparse_amf(SV *data, SV* sv_option = 0) ALIAS: Storable::AMF::deparse_amf3 = 1 PROTOTYPE: $;$ INIT: SV* retvalue; struct io_struct io[1]; PPCODE: PERL_UNUSED_VAR(ix); if ( ! Sigsetjmp(io->target_error, 0)){ io->subname = "Storable::AMF3::deparse_amf( data, option )"; io_in_init(aTHX_ io, data, AMF3_VERSION, sv_option); retvalue = (SV*) (amf3_parse_one(aTHX_ io)); /* clean up storable unless need */ if ( io->reuse ) io_in_cleanup(aTHX_ io); sv_2mortal(retvalue); sv_setsv(ERRSV, &PL_sv_undef); XPUSHs(retvalue); if (GIMME_V == G_ARRAY){ XPUSHs( sv_2mortal(newSViv( io->pos - io->ptr )) ); } } else { io_format_error(aTHX_ io ); } void thaw(SV *data, SV *sv_option = 0) PROTOTYPE: $;$ INIT: SV* retvalue; struct io_struct io[1]; ALIAS: Storable::AMF::thaw3=1 PPCODE: PERL_UNUSED_VAR(ix); if ( ! Sigsetjmp(io->target_error, 0)){ io->subname = "Storable::AMF3::thaw( data, option )"; io_in_init(aTHX_ io, data, AMF3_VERSION, sv_option); retvalue = (SV*) (amf3_parse_one(aTHX_ io)); /* clean up storable unless need */ if ( io->reuse ) io_in_cleanup(aTHX_ io); sv_2mortal(retvalue); io_test_eof( aTHX_ io ); sv_setsv(ERRSV, &PL_sv_undef); XPUSHs(retvalue); } else { io_format_error(aTHX_ io); } void _test_thaw_integer(SV*data) INIT: SV* retvalue; struct io_struct io[1]; PPCODE: if ( ! Sigsetjmp(io->target_error, 0)){ io->subname = "Storable::AMF3::_test_thaw_integer( data, option )"; io_in_init(aTHX_ io, data, AMF3_VERSION, 0 ); retvalue = (SV*) (amf3_parse_integer(aTHX_ io)); sv_2mortal(retvalue); io_test_eof( aTHX_ io ); sv_setsv(ERRSV, &PL_sv_undef); XPUSHs(retvalue); } else { io_format_error(aTHX_ io ); } void _test_freeze_integer(SV*data) PREINIT: SV * retvalue; struct io_struct io[1]; PPCODE: if (! Sigsetjmp(io->target_error, 0)){ io_out_init(aTHX_ io, 0, AMF3_VERSION); amf3_write_integer(aTHX_ io, SvIV(data)); retvalue = io_buffer(io); XPUSHs(retvalue); sv_setsv(ERRSV, &PL_sv_undef); } else { io_format_error( aTHX_ io ); } void endian() PPCODE: PerlIO_printf(PerlIO_stderr(), "%s %x\n", GAX, BYTEORDER); void freeze(SV *data, SV *sv_option = 0 ) PROTOTYPE: $;$ PREINIT: SV * retvalue; struct io_struct io[1]; ALIAS: Storable::AMF::freeze3=1 PPCODE: PERL_UNUSED_VAR(ix); if (! Sigsetjmp(io->target_error, 0)){ io_out_init(aTHX_ io, sv_option, AMF3_VERSION); amf3_format_one(aTHX_ io, data); if (io->reuse ) io_out_cleanup(aTHX_ io); retvalue = io_buffer(io); XPUSHs(retvalue); sv_setsv(ERRSV, &PL_sv_undef); } else { io_format_error( aTHX_ io ); } void new_amfdate(NV timestamp ) PREINIT: SV *mortal; PROTOTYPE: $ ALIAS: Storable::AMF::new_amfdate =1 Storable::AMF0::new_amfdate=2 Storable::AMF::new_date =3 Storable::AMF0::new_date=4 Storable::AMF3::new_date=5 PPCODE: PERL_UNUSED_VAR( ix ); mortal=sv_newmortal(); sv_setref_nv( mortal, "*", timestamp ); /*Stupid but it works */ XPUSHs( mortal ); void perl_date(SV *date) PREINIT: SV *mortal; PROTOTYPE: $ ALIAS: Storable::AMF::perl_date=1 Storable::AMF0::perl_date=2 PPCODE: PERL_UNUSED_VAR( ix ); if ( SvROK( date ) && util_is_date( (SV*) SvRV(date))){ XPUSHs((SV*) SvRV(date)); } else if ( SvNOK( date )){ mortal = sv_newmortal(); sv_setnv( mortal, SvNV( date )); XPUSHs(mortal); } else { croak("Expecting perl/amf date as argument" ); } void parse_option(char * s, int options=0) PREINIT: int s_strict; int s_utf8_decode; int s_utf8_encode; int s_milldate; int s_raise_error; int s_prefer_number; int s_ext_boolean; /* I8 -> int*/ int s_targ; int sign; char *word; char *current; bool error; PROTOTYPE: $;$ ALIAS: Storable::AMF::parse_option=1 Storable::AMF0::parse_option=2 Storable::AMF::parse_serializator_option=3 Storable::AMF3::parse_serializator_option=4 Storable::AMF0::parse_serializator_option=5 PPCODE: PERL_UNUSED_VAR( ix ); s_strict = 0; s_utf8_decode = 0; s_utf8_encode = 0; s_milldate = 0; s_raise_error = 0; s_prefer_number = 0; s_ext_boolean = 0; options = 0; s_targ = 1; for( current = s;*current && ( !isALPHA( *current ) && *current!='+' && *current!='-' ) ; ++current ); word = current; while( *word ){ ++current; error = 0; sign = 1; if ( *word == '+' ){ ++word; } else if ( *word =='-' ){ sign = -1; ++word; } for( ; *current && ( isALNUM( *current ) || *current == '_' ); ++current ); switch( current - word ){ case 4: if ( !strncmp( "targ", word, 4)){ s_targ = sign; } else { error = 1; }; break; case 6: if (!strncmp("strict", word, 6)){ s_strict = sign; } else { error = 1; }; break; case 11: if (!strncmp( "utf8_decode", word, 11)){ s_utf8_decode = sign; } else if (!strncmp( "utf8_encode", word, 11)){ s_utf8_encode = sign; } else if (!strncmp("raise_error", word, 9)){ s_raise_error=sign; } else { error = 1; } break; case 13: if (!strncmp( "prefer_number", word, 13)){ s_prefer_number = sign; } else { error = 1; }; break; case 12: if (!strncmp("json_boolean", word, 12)){ s_ext_boolean = sign; } else if (!strncmp("boolean_json", word, 12)){ s_ext_boolean = sign; } else error = 1; break; case 16: if (!strncmp("millisecond_date", word, 16)){ s_milldate = sign; } else error = 1; break; default: error = 1; }; if (error) croak("Storable::AMF0::parse_option: unknown option '%.*s'", current - word, word); for(; *current && !isALPHA(*current) && *current!='+' && *current!='-'; ++current); word = current; }; SIGN_BOOL_APPLY( options, s_strict, OPT_STRICT ); SIGN_BOOL_APPLY( options, s_milldate, OPT_MILLSEC_DATE ); SIGN_BOOL_APPLY( options, s_utf8_decode, OPT_DECODE_UTF8 ); SIGN_BOOL_APPLY( options, s_utf8_encode, OPT_ENCODE_UTF8 ); SIGN_BOOL_APPLY( options, s_raise_error, OPT_RAISE_ERROR ); SIGN_BOOL_APPLY( options, s_prefer_number, OPT_PREFER_NUMBER ); SIGN_BOOL_APPLY( options, s_ext_boolean, OPT_JSON_BOOLEAN ); SIGN_BOOL_APPLY( options, s_targ, OPT_TARG ); mXPUSHi( options ); MODULE = Storable::AMF0 PACKAGE = Storable::AMF::Util void total_sv() PPCODE: I32 visited = 0; SV* sva; for( sva = PL_sv_arenaroot; sva; sva = (SV*)SvANY(sva)) { SV * svend = &sva[SvREFCNT(sva)]; SV * svi; /* fprintf( stderr, "=%p %d\n", sva, SvREFCNT( sva ) ); */ for( svi = sva + 1; svitarget_error, 0) ){ io->subname = "Storable::AMF0::thaw( data, option )"; io_in_init(aTHX_ io, data, AMF0_VERSION, sv_option); retvalue = (SV*) (amf0_parse_one_tmp( aTHX_ io, element )); /* clean up storable unless need */ if ( io->reuse ) io_in_cleanup(aTHX_ io); retvalue = sv_2mortal(retvalue); io_test_eof( aTHX_ io ); sv_setsv(ERRSV, &PL_sv_undef); /* XPUSHs(retvalue); */ } else { io_format_error( aTHX_ io ); } MODULE=Storable::AMF