@ -182,11 +182,19 @@ static bool putkey(void *closure, const void *handler_data) {
return true ; \
} \
static bool repeated_ # # type ( void * closure , const void * handler_data , \
type val ) { \
type val ) { \
upb_json_printer * p = closure ; \
print_comma ( p ) ; \
CHK ( put # # type ( closure , handler_data , val ) ) ; \
return true ; \
} \
static bool putmapkey_ # # type ( void * closure , const void * handler_data , \
type val ) { \
upb_json_printer * p = closure ; \
print_data ( p , " \" " , 1 ) ; \
CHK ( put # # type ( closure , handler_data , val ) ) ; \
print_data ( p , " \" : " , 2 ) ; \
return true ; \
}
TYPE_HANDLERS ( double , fmt_double ) ;
@ -222,20 +230,36 @@ static bool scalar_enum(void *closure, const void *handler_data,
return true ;
}
static bool repeated_enum ( void * closure , const void * handler_data ,
int32_t val ) {
const EnumHandlerData * hd = handler_data ;
upb_json_printer * p = closure ;
print_comma ( p ) ;
const char * symbolic_name = upb_enumdef_iton ( hd - > enumdef , val ) ;
static void print_enum_symbolic_name ( upb_json_printer * p ,
const upb_enumdef * def ,
int32_t val ) {
const char * symbolic_name = upb_enumdef_iton ( def , val ) ;
if ( symbolic_name ) {
print_data ( p , " \" " , 1 ) ;
putstring ( p , symbolic_name , strlen ( symbolic_name ) ) ;
print_data ( p , " \" " , 1 ) ;
} else {
putint32_t ( closure , NULL , val ) ;
putint32_t ( p , NULL , val ) ;
}
}
static bool repeated_enum ( void * closure , const void * handler_data ,
int32_t val ) {
const EnumHandlerData * hd = handler_data ;
upb_json_printer * p = closure ;
print_comma ( p ) ;
print_enum_symbolic_name ( p , hd - > enumdef , val ) ;
return true ;
}
static bool mapvalue_enum ( void * closure , const void * handler_data ,
int32_t val ) {
const EnumHandlerData * hd = handler_data ;
upb_json_printer * p = closure ;
print_enum_symbolic_name ( p , hd - > enumdef , val ) ;
return true ;
}
@ -251,25 +275,35 @@ static void *repeated_startsubmsg(void *closure, const void *handler_data) {
return closure ;
}
static bool startmap ( void * closure , const void * handler_data ) {
static void start_frame ( upb_json_printer * p ) {
p - > depth_ + + ;
p - > first_elem_ [ p - > depth_ ] = true ;
print_data ( p , " { " , 1 ) ;
}
static void end_frame ( upb_json_printer * p ) {
print_data ( p , " } " , 1 ) ;
p - > depth_ - - ;
}
static bool printer_startmsg ( void * closure , const void * handler_data ) {
UPB_UNUSED ( handler_data ) ;
upb_json_printer * p = closure ;
if ( p - > depth_ + + = = 0 ) {
if ( p - > depth_ = = 0 ) {
upb_bytessink_start ( p - > output_ , 0 , & p - > subc_ ) ;
}
p - > first_elem_ [ p - > depth_ ] = true ;
print_data ( p , " { " , 1 ) ;
start_frame ( p ) ;
return true ;
}
static bool endmap ( void * closure , const void * handler_data , upb_status * s ) {
static bool printer_endmsg ( void * closure , const void * handler_data , upb_status * s ) {
UPB_UNUSED ( handler_data ) ;
UPB_UNUSED ( s ) ;
upb_json_printer * p = closure ;
if ( - - p - > depth_ = = 0 ) {
end_frame ( p ) ;
if ( p - > depth_ = = 0 ) {
upb_bytessink_end ( p - > output_ ) ;
}
print_data ( p , " } " , 1 ) ;
return true ;
}
@ -290,6 +324,23 @@ static bool endseq(void *closure, const void *handler_data) {
return true ;
}
static void * startmap ( void * closure , const void * handler_data ) {
upb_json_printer * p = closure ;
CHK ( putkey ( closure , handler_data ) ) ;
p - > depth_ + + ;
p - > first_elem_ [ p - > depth_ ] = true ;
print_data ( p , " { " , 1 ) ;
return closure ;
}
static bool endmap ( void * closure , const void * handler_data ) {
UPB_UNUSED ( handler_data ) ;
upb_json_printer * p = closure ;
print_data ( p , " } " , 1 ) ;
p - > depth_ - - ;
return true ;
}
static size_t putstr ( void * closure , const void * handler_data , const char * str ,
size_t len , const upb_bufhandle * handle ) {
UPB_UNUSED ( handler_data ) ;
@ -404,6 +455,36 @@ static bool repeated_endstr(void *closure, const void *handler_data) {
return true ;
}
static void * mapkeyval_startstr ( void * closure , const void * handler_data ,
size_t size_hint ) {
UPB_UNUSED ( handler_data ) ;
UPB_UNUSED ( size_hint ) ;
upb_json_printer * p = closure ;
print_data ( p , " \" " , 1 ) ;
return p ;
}
static size_t mapkey_str ( void * closure , const void * handler_data ,
const char * str , size_t len ,
const upb_bufhandle * handle ) {
CHK ( putstr ( closure , handler_data , str , len , handle ) ) ;
return len ;
}
static bool mapkey_endstr ( void * closure , const void * handler_data ) {
UPB_UNUSED ( handler_data ) ;
upb_json_printer * p = closure ;
print_data ( p , " \" : " , 2 ) ;
return true ;
}
static bool mapvalue_endstr ( void * closure , const void * handler_data ) {
UPB_UNUSED ( handler_data ) ;
upb_json_printer * p = closure ;
print_data ( p , " \" " , 1 ) ;
return true ;
}
static size_t scalar_bytes ( void * closure , const void * handler_data ,
const char * str , size_t len ,
const upb_bufhandle * handle ) {
@ -421,31 +502,161 @@ static size_t repeated_bytes(void *closure, const void *handler_data,
return len ;
}
void printer_sethandlers ( const void * closure , upb_handlers * h ) {
static size_t mapkey_bytes ( void * closure , const void * handler_data ,
const char * str , size_t len ,
const upb_bufhandle * handle ) {
upb_json_printer * p = closure ;
CHK ( putbytes ( closure , handler_data , str , len , handle ) ) ;
print_data ( p , " : " , 1 ) ;
return len ;
}
static void set_enum_hd ( upb_handlers * h ,
const upb_fielddef * f ,
upb_handlerattr * attr ) {
EnumHandlerData * hd = malloc ( sizeof ( EnumHandlerData ) ) ;
hd - > enumdef = ( const upb_enumdef * ) upb_fielddef_subdef ( f ) ;
hd - > keyname = newstrpc ( h , f ) ;
upb_handlers_addcleanup ( h , hd , free ) ;
upb_handlerattr_sethandlerdata ( attr , hd ) ;
}
// Set up handlers for a mapentry submessage (i.e., an individual key/value pair
// in a map).
//
// TODO: Handle missing key, missing value, out-of-order key/value, or repeated
// key or value cases properly. The right way to do this is to allocate a
// temporary structure at the start of a mapentry submessage, store key and
// value data in it as key and value handlers are called, and then print the
// key/value pair once at the end of the submessage. If we don't do this, we
// should at least detect the case and throw an error. However, so far all of
// our sources that emit mapentry messages do so canonically (with one key
// field, and then one value field), so this is not a pressing concern at the
// moment.
void printer_sethandlers_mapentry ( const void * closure , upb_handlers * h ) {
UPB_UNUSED ( closure ) ;
const upb_msgdef * md = upb_handlers_msgdef ( h ) ;
// A mapentry message is printed simply as '"key": value'. Rather than
// special-case key and value for every type below, we just handle both
// fields explicitly here.
const upb_fielddef * key_field = upb_msgdef_itof ( md , UPB_MAPENTRY_KEY ) ;
const upb_fielddef * value_field = upb_msgdef_itof ( md , UPB_MAPENTRY_VALUE ) ;
upb_handlerattr empty_attr = UPB_HANDLERATTR_INITIALIZER ;
upb_handlers_setstartmsg ( h , startmap , & empty_attr ) ;
upb_handlers_setendmsg ( h , endmap , & empty_attr ) ;
# define TYPE(type, name, ctype) \
case type : \
if ( upb_fielddef_isseq ( f ) ) { \
upb_handlers_set # # name ( h , f , repeated_ # # ctype , & empty_attr ) ; \
} else { \
upb_handlers_set # # name ( h , f , scalar_ # # ctype , & name_attr ) ; \
} \
switch ( upb_fielddef_type ( key_field ) ) {
case UPB_TYPE_INT32 :
upb_handlers_setint32 ( h , key_field , putmapkey_int32_t , & empty_attr ) ;
break ;
case UPB_TYPE_INT64 :
upb_handlers_setint64 ( h , key_field , putmapkey_int64_t , & empty_attr ) ;
break ;
case UPB_TYPE_UINT32 :
upb_handlers_setuint32 ( h , key_field , putmapkey_uint32_t , & empty_attr ) ;
break ;
case UPB_TYPE_UINT64 :
upb_handlers_setuint64 ( h , key_field , putmapkey_uint64_t , & empty_attr ) ;
break ;
case UPB_TYPE_BOOL :
upb_handlers_setbool ( h , key_field , putmapkey_bool , & empty_attr ) ;
break ;
case UPB_TYPE_STRING :
upb_handlers_setstartstr ( h , key_field , mapkeyval_startstr , & empty_attr ) ;
upb_handlers_setstring ( h , key_field , mapkey_str , & empty_attr ) ;
upb_handlers_setendstr ( h , key_field , mapkey_endstr , & empty_attr ) ;
break ;
case UPB_TYPE_BYTES :
upb_handlers_setstring ( h , key_field , mapkey_bytes , & empty_attr ) ;
break ;
default :
assert ( false ) ;
break ;
}
switch ( upb_fielddef_type ( value_field ) ) {
case UPB_TYPE_INT32 :
upb_handlers_setint32 ( h , value_field , putint32_t , & empty_attr ) ;
break ;
case UPB_TYPE_INT64 :
upb_handlers_setint64 ( h , value_field , putint64_t , & empty_attr ) ;
break ;
case UPB_TYPE_UINT32 :
upb_handlers_setuint32 ( h , value_field , putuint32_t , & empty_attr ) ;
break ;
case UPB_TYPE_UINT64 :
upb_handlers_setuint64 ( h , value_field , putuint64_t , & empty_attr ) ;
break ;
case UPB_TYPE_BOOL :
upb_handlers_setbool ( h , value_field , putbool , & empty_attr ) ;
break ;
case UPB_TYPE_FLOAT :
upb_handlers_setfloat ( h , value_field , putfloat , & empty_attr ) ;
break ;
case UPB_TYPE_DOUBLE :
upb_handlers_setdouble ( h , value_field , putdouble , & empty_attr ) ;
break ;
case UPB_TYPE_STRING :
upb_handlers_setstartstr ( h , value_field , mapkeyval_startstr , & empty_attr ) ;
upb_handlers_setstring ( h , value_field , putstr , & empty_attr ) ;
upb_handlers_setendstr ( h , value_field , mapvalue_endstr , & empty_attr ) ;
break ;
case UPB_TYPE_BYTES :
upb_handlers_setstring ( h , value_field , putbytes , & empty_attr ) ;
break ;
case UPB_TYPE_ENUM : {
upb_handlerattr enum_attr = UPB_HANDLERATTR_INITIALIZER ;
set_enum_hd ( h , value_field , & enum_attr ) ;
upb_handlers_setint32 ( h , value_field , mapvalue_enum , & enum_attr ) ;
upb_handlerattr_uninit ( & enum_attr ) ;
break ;
}
case UPB_TYPE_MESSAGE :
// No handler necessary -- the submsg handlers will print the message
// as appropriate.
break ;
}
upb_handlerattr_uninit ( & empty_attr ) ;
}
void printer_sethandlers ( const void * closure , upb_handlers * h ) {
UPB_UNUSED ( closure ) ;
const upb_msgdef * md = upb_handlers_msgdef ( h ) ;
bool is_mapentry = upb_msgdef_mapentry ( md ) ;
upb_handlerattr empty_attr = UPB_HANDLERATTR_INITIALIZER ;
if ( is_mapentry ) {
// mapentry messages are sufficiently different that we handle them
// separately.
printer_sethandlers_mapentry ( closure , h ) ;
return ;
}
upb_handlers_setstartmsg ( h , printer_startmsg , & empty_attr ) ;
upb_handlers_setendmsg ( h , printer_endmsg , & empty_attr ) ;
# define TYPE(type, name, ctype) \
case type : \
if ( upb_fielddef_isseq ( f ) ) { \
upb_handlers_set # # name ( h , f , repeated_ # # ctype , & empty_attr ) ; \
} else { \
upb_handlers_set # # name ( h , f , scalar_ # # ctype , & name_attr ) ; \
} \
break ;
upb_msg_field_iter i ;
upb_msg_field_begin ( & i , upb_handlers_msgdef ( h ) ) ;
upb_msg_field_begin ( & i , md ) ;
for ( ; ! upb_msg_field_done ( & i ) ; upb_msg_field_next ( & i ) ) {
const upb_fielddef * f = upb_msg_iter_field ( & i ) ;
upb_handlerattr name_attr = UPB_HANDLERATTR_INITIALIZER ;
upb_handlerattr_sethandlerdata ( & name_attr , newstrpc ( h , f ) ) ;
if ( upb_fielddef_isseq ( f ) ) {
if ( upb_fielddef_ismap ( f ) ) {
upb_handlers_setstartseq ( h , f , startmap , & name_attr ) ;
upb_handlers_setendseq ( h , f , endmap , & name_attr ) ;
} else if ( upb_fielddef_isseq ( f ) ) {
upb_handlers_setstartseq ( h , f , startseq , & name_attr ) ;
upb_handlers_setendseq ( h , f , endseq , & empty_attr ) ;
}
@ -462,12 +673,8 @@ void printer_sethandlers(const void *closure, upb_handlers *h) {
// For now, we always emit symbolic names for enums. We may want an
// option later to control this behavior, but we will wait for a real
// need first.
EnumHandlerData * hd = malloc ( sizeof ( EnumHandlerData ) ) ;
hd - > enumdef = ( const upb_enumdef * ) upb_fielddef_subdef ( f ) ;
hd - > keyname = newstrpc ( h , f ) ;
upb_handlers_addcleanup ( h , hd , free ) ;
upb_handlerattr enum_attr = UPB_HANDLERATTR_INITIALIZER ;
upb_handlerattr_sethandlerdata ( & enum_attr , hd ) ;
set_enum_hd ( h , f , & enum_attr ) ;
if ( upb_fielddef_isseq ( f ) ) {
upb_handlers_setint32 ( h , f , repeated_enum , & enum_attr ) ;