@ -60,6 +60,28 @@ static bool consumes_input(opcode op) {
}
}
}
}
static size_t stacksize ( upb_pbdecoder * d , size_t entries ) {
UPB_UNUSED ( d ) ;
return entries * sizeof ( upb_pbdecoder_frame ) ;
}
static size_t callstacksize ( upb_pbdecoder * d , size_t entries ) {
UPB_UNUSED ( d ) ;
# ifdef UPB_USE_JIT_X64
if ( d - > method_ - > is_native_ ) {
/* Each native stack frame needs two pointers, plus we need a few frames for
* the enter / exit trampolines . */
size_t ret = entries * sizeof ( void * ) * 2 ;
ret + = sizeof ( void * ) * 10 ;
return ret ;
}
# endif
return entries * sizeof ( uint32_t * ) ;
}
static bool in_residual_buf ( const upb_pbdecoder * d , const char * p ) ;
static bool in_residual_buf ( const upb_pbdecoder * d , const char * p ) ;
/* It's unfortunate that we have to micro-manage the compiler with
/* It's unfortunate that we have to micro-manage the compiler with
@ -145,24 +167,66 @@ static void checkpoint(upb_pbdecoder *d) {
d - > checkpoint = d - > ptr ;
d - > checkpoint = d - > ptr ;
}
}
/* Skips "bytes" bytes in the stream, which may be more than available. If we
* skip more bytes than are available , we return a long read count to the caller
* indicating how many bytes can be skipped over before passing actual data
* again . Skipped bytes can pass a NULL buffer and the decoder guarantees they
* won ' t actually be read .
*/
static int32_t skip ( upb_pbdecoder * d , size_t bytes ) {
assert ( ! in_residual_buf ( d , d - > ptr ) | | d - > size_param = = 0 ) ;
if ( curbufleft ( d ) > bytes ) {
/* Skipped data is all in current buffer, and more is still available. */
advance ( d , bytes ) ;
d - > skip = 0 ;
return DECODE_OK ;
} else {
/* Skipped data extends beyond currently available buffers. */
d - > pc = d - > last ;
d - > skip = bytes - curbufleft ( d ) ;
d - > bufstart_ofs + = ( d - > end - d - > buf ) ;
d - > residual_end = d - > residual ;
switchtobuf ( d , d - > residual , d - > residual_end ) ;
return d - > size_param + d - > skip ;
}
}
/* Resumes the decoder from an initial state or from a previous suspend. */
/* Resumes the decoder from an initial state or from a previous suspend. */
int32_t upb_pbdecoder_resume ( upb_pbdecoder * d , void * p , const char * buf ,
int32_t upb_pbdecoder_resume ( upb_pbdecoder * d , void * p , const char * buf ,
size_t size , const upb_bufhandle * handle ) {
size_t size , const upb_bufhandle * handle ) {
UPB_UNUSED ( p ) ; /* Useless; just for the benefit of the JIT. */
UPB_UNUSED ( p ) ; /* Useless; just for the benefit of the JIT. */
d - > buf_param = buf ;
d - > buf_param = buf ;
d - > size_param = size ;
d - > size_param = size ;
d - > handle = handle ;
d - > handle = handle ;
if ( d - > residual_end > d - > residual ) {
if ( d - > residual_end > d - > residual ) {
/* We have residual bytes from the last buffer. */
/* We have residual bytes from the last buffer. */
assert ( d - > ptr = = d - > residual ) ;
assert ( d - > ptr = = d - > residual ) ;
} else {
} else {
switchtobuf ( d , buf , buf + size ) ;
switchtobuf ( d , buf , buf + size ) ;
}
}
d - > checkpoint = d - > ptr ;
d - > checkpoint = d - > ptr ;
if ( d - > skip ) {
CHECK_RETURN ( skip ( d , d - > skip ) ) ;
d - > checkpoint = d - > ptr ;
}
if ( ! buf ) {
/* NULL buf is ok if its entire span is covered by the "skip" above, but
* by this point we know that " skip " doesn ' t cover the buffer . */
seterr ( d , " Passed NULL buffer over non-skippable region. " ) ;
return upb_pbdecoder_suspend ( d ) ;
}
if ( d - > top - > groupnum < 0 ) {
if ( d - > top - > groupnum < 0 ) {
CHECK_RETURN ( upb_pbdecoder_skipunknown ( d , - 1 , 0 ) ) ;
CHECK_RETURN ( upb_pbdecoder_skipunknown ( d , - 1 , 0 ) ) ;
d - > checkpoint = d - > ptr ;
d - > checkpoint = d - > ptr ;
}
}
return DECODE_OK ;
return DECODE_OK ;
}
}
@ -222,28 +286,6 @@ static size_t suspend_save(upb_pbdecoder *d) {
return d - > size_param ;
return d - > size_param ;
}
}
/* Skips "bytes" bytes in the stream, which may be more than available. If we
* skip more bytes than are available , we return a long read count to the caller
* indicating how many bytes the caller should skip before passing a new buffer .
*/
static int32_t skip ( upb_pbdecoder * d , size_t bytes ) {
assert ( ! in_residual_buf ( d , d - > ptr ) | | d - > size_param = = 0 ) ;
if ( curbufleft ( d ) > = bytes ) {
/* Skipped data is all in current buffer. */
advance ( d , bytes ) ;
return DECODE_OK ;
} else {
/* Skipped data extends beyond currently available buffers. */
size_t skip ;
d - > pc = d - > last ;
skip = bytes - curbufleft ( d ) ;
d - > bufstart_ofs + = ( d - > end - d - > buf ) + skip ;
d - > residual_end = d - > residual ;
switchtobuf ( d , d - > residual , d - > residual_end ) ;
return d - > size_param + skip ;
}
}
/* Copies the next "bytes" bytes into "buf" and advances the stream.
/* Copies the next "bytes" bytes into "buf" and advances the stream.
* Requires that this many bytes are available in the current buffer . */
* Requires that this many bytes are available in the current buffer . */
UPB_FORCEINLINE static void consumebytes ( upb_pbdecoder * d , void * buf ,
UPB_FORCEINLINE static void consumebytes ( upb_pbdecoder * d , void * buf ,
@ -618,18 +660,8 @@ upb_pbdecoder_frame *outer_frame(upb_pbdecoder *d) {
/* The main decoder VM function. Uses traditional bytecode dispatch loop with a
/* The main decoder VM function. Uses traditional bytecode dispatch loop with a
* switch ( ) statement . */
* switch ( ) statement . */
size_t upb_pbdecoder_decode ( void * closure , const void * hd , const char * buf ,
size_t run_decoder_vm ( upb_pbdecoder * d , const mgroup * group ,
size_t size , const upb_bufhandle * handle ) {
const upb_bufhandle * handle ) {
upb_pbdecoder * d = closure ;
const mgroup * group = hd ;
int32_t result ;
assert ( buf ) ;
result = upb_pbdecoder_resume ( d , NULL , buf , size , handle ) ;
if ( result = = DECODE_ENDGROUP ) {
goto_endmsg ( d ) ;
}
CHECK_RETURN ( result ) ;
UPB_UNUSED ( group ) ;
# define VMCASE(op, code) \
# define VMCASE(op, code) \
case op : { code ; if ( consumes_input ( op ) ) checkpoint ( d ) ; break ; }
case op : { code ; if ( consumes_input ( op ) ) checkpoint ( d ) ; break ; }
@ -652,6 +684,7 @@ size_t upb_pbdecoder_decode(void *closure, const void *hd, const char *buf,
arg = instruction > > 8 ;
arg = instruction > > 8 ;
longofs = arg ;
longofs = arg ;
assert ( d - > ptr ! = d - > residual_end ) ;
assert ( d - > ptr ! = d - > residual_end ) ;
UPB_UNUSED ( group ) ;
# ifdef UPB_DUMP_BYTECODE
# ifdef UPB_DUMP_BYTECODE
fprintf ( stderr , " s_ofs=%d buf_ofs=%d data_rem=%d buf_rem=%d delim_rem=%d "
fprintf ( stderr , " s_ofs=%d buf_ofs=%d data_rem=%d buf_rem=%d delim_rem=%d "
" %x %s (%d) \n " ,
" %x %s (%d) \n " ,
@ -827,12 +860,15 @@ size_t upb_pbdecoder_decode(void *closure, const void *hd, const char *buf,
CHECK_RETURN ( dispatch ( d ) ) ;
CHECK_RETURN ( dispatch ( d ) ) ;
} )
} )
VMCASE ( OP_HALT , {
VMCASE ( OP_HALT , {
return size ;
return d - > size_param ;
} )
} )
}
}
}
}
}
}
/* BytesHandler handlers ******************************************************/
void * upb_pbdecoder_startbc ( void * closure , const void * pc , size_t size_hint ) {
void * upb_pbdecoder_startbc ( void * closure , const void * pc , size_t size_hint ) {
upb_pbdecoder * d = closure ;
upb_pbdecoder * d = closure ;
UPB_UNUSED ( size_hint ) ;
UPB_UNUSED ( size_hint ) ;
@ -841,6 +877,7 @@ void *upb_pbdecoder_startbc(void *closure, const void *pc, size_t size_hint) {
d - > call_len = 1 ;
d - > call_len = 1 ;
d - > callstack [ 0 ] = & halt ;
d - > callstack [ 0 ] = & halt ;
d - > pc = pc ;
d - > pc = pc ;
d - > skip = 0 ;
return d ;
return d ;
}
}
@ -851,6 +888,7 @@ void *upb_pbdecoder_startjit(void *closure, const void *hd, size_t size_hint) {
d - > top - > end_ofs = UINT64_MAX ;
d - > top - > end_ofs = UINT64_MAX ;
d - > bufstart_ofs = 0 ;
d - > bufstart_ofs = 0 ;
d - > call_len = 0 ;
d - > call_len = 0 ;
d - > skip = 0 ;
return d ;
return d ;
}
}
@ -859,12 +897,9 @@ bool upb_pbdecoder_end(void *closure, const void *handler_data) {
const upb_pbdecodermethod * method = handler_data ;
const upb_pbdecodermethod * method = handler_data ;
uint64_t end ;
uint64_t end ;
char dummy ;
char dummy ;
# ifdef UPB_USE_JIT_X64
const mgroup * group = ( const mgroup * ) method - > group ;
# endif
if ( d - > residual_end > d - > residual ) {
if ( d - > residual_end > d - > residual ) {
seterr ( d , " Unexpected EOF " ) ;
seterr ( d , " Unexpected EOF: decoder still has buffered unparsed data " ) ;
return false ;
return false ;
}
}
@ -873,15 +908,15 @@ bool upb_pbdecoder_end(void *closure, const void *handler_data) {
return false ;
return false ;
}
}
/* M essage ends here. */
/* The user's end() call indicates that the m essage ends here. */
end = offset ( d ) ;
end = offset ( d ) ;
d - > top - > end_ofs = end ;
d - > top - > end_ofs = end ;
# ifdef UPB_USE_JIT_X64
# ifdef UPB_USE_JIT_X64
if ( group - > jit_code ) {
if ( method - > group - > jit_code ) {
if ( d - > top ! = d - > stack )
if ( d - > top ! = d - > stack )
d - > stack - > end_ofs = 0 ;
d - > stack - > end_ofs = 0 ;
group - > jit_code ( closure , method - > code_base . ptr , & dummy , 0 , NULL ) ;
method - > group - > jit_code ( closure , method - > code_base . ptr , & dummy , 0 , NULL ) ;
} else
} else
# endif
# endif
{
{
@ -901,13 +936,26 @@ bool upb_pbdecoder_end(void *closure, const void *handler_data) {
}
}
if ( d - > call_len ! = 0 ) {
if ( d - > call_len ! = 0 ) {
seterr ( d , " Unexpected EOF " ) ;
seterr ( d , " Unexpected EOF inside submessage or group " ) ;
return false ;
return false ;
}
}
return true ;
return true ;
}
}
size_t upb_pbdecoder_decode ( void * decoder , const void * group , const char * buf ,
size_t size , const upb_bufhandle * handle ) {
int32_t result = upb_pbdecoder_resume ( decoder , NULL , buf , size , handle ) ;
if ( result = = DECODE_ENDGROUP ) goto_endmsg ( decoder ) ;
CHECK_RETURN ( result ) ;
return run_decoder_vm ( decoder , group , handle ) ;
}
/* Public API *****************************************************************/
void upb_pbdecoder_reset ( upb_pbdecoder * d ) {
void upb_pbdecoder_reset ( upb_pbdecoder * d ) {
d - > top = d - > stack ;
d - > top = d - > stack ;
d - > top - > groupnum = 0 ;
d - > top - > groupnum = 0 ;
@ -917,27 +965,6 @@ void upb_pbdecoder_reset(upb_pbdecoder *d) {
d - > residual_end = d - > residual ;
d - > residual_end = d - > residual ;
}
}
static size_t stacksize ( upb_pbdecoder * d , size_t entries ) {
UPB_UNUSED ( d ) ;
return entries * sizeof ( upb_pbdecoder_frame ) ;
}
static size_t callstacksize ( upb_pbdecoder * d , size_t entries ) {
UPB_UNUSED ( d ) ;
# ifdef UPB_USE_JIT_X64
if ( d - > method_ - > is_native_ ) {
/* Each native stack frame needs two pointers, plus we need a few frames for
* the enter / exit trampolines . */
size_t ret = entries * sizeof ( void * ) * 2 ;
ret + = sizeof ( void * ) * 10 ;
return ret ;
}
# endif
return entries * sizeof ( uint32_t * ) ;
}
upb_pbdecoder * upb_pbdecoder_create ( upb_env * e , const upb_pbdecodermethod * m ,
upb_pbdecoder * upb_pbdecoder_create ( upb_env * e , const upb_pbdecodermethod * m ,
upb_sink * sink ) {
upb_sink * sink ) {
const size_t default_max_nesting = 64 ;
const size_t default_max_nesting = 64 ;