2017-10-09 12:52:09 +02:00
#! /usr/bin/env python3
2016-11-29 20:51:50 +01:00
# Read from stdin, spit out C header or body.
2017-01-06 03:54:45 +01:00
import argparse
2017-11-03 01:10:57 +01:00
import copy
2016-11-29 20:51:50 +01:00
import fileinput
import re
2018-02-21 19:46:04 +01:00
from collections import namedtuple
2016-11-29 20:51:50 +01:00
Enumtype = namedtuple ( ' Enumtype ' , [ ' name ' , ' value ' ] )
2017-02-28 23:07:38 +01:00
type2size = {
' pad ' : 1 ,
' struct channel_id ' : 32 ,
' struct short_channel_id ' : 8 ,
' struct ipv6 ' : 16 ,
' secp256k1_ecdsa_signature ' : 64 ,
2017-03-29 12:56:15 +02:00
' struct preimage ' : 32 ,
2017-02-28 23:07:38 +01:00
' struct pubkey ' : 33 ,
2019-04-08 11:58:44 +02:00
' struct node_id ' : 33 ,
2017-02-28 23:07:38 +01:00
' struct sha256 ' : 32 ,
2017-12-18 07:44:10 +01:00
' struct bitcoin_blkid ' : 32 ,
2017-12-18 07:41:52 +01:00
' struct bitcoin_txid ' : 32 ,
2018-07-09 13:17:58 +02:00
' struct secret ' : 32 ,
2019-02-21 04:45:55 +01:00
' struct amount_msat ' : 8 ,
' struct amount_sat ' : 8 ,
2017-02-28 23:07:38 +01:00
' u64 ' : 8 ,
' u32 ' : 4 ,
' u16 ' : 2 ,
' u8 ' : 1 ,
2019-04-03 04:30:48 +02:00
' bool ' : 1 ,
' var_int ' : 8 ,
2017-02-28 23:07:38 +01:00
}
2017-03-18 15:50:56 +01:00
# These struct array helpers require a context to allocate from.
varlen_structs = [
2018-07-24 08:18:59 +02:00
' peer_features ' ,
2017-03-18 15:50:56 +01:00
' gossip_getnodes_entry ' ,
2019-04-08 11:58:44 +02:00
' gossip_getchannels_entry ' ,
2017-06-20 07:46:03 +02:00
' failed_htlc ' ,
2018-01-02 14:54:32 +01:00
' utxo ' ,
2018-02-08 02:25:12 +01:00
' bitcoin_tx ' ,
2018-02-08 02:25:12 +01:00
' wirestring ' ,
2017-03-18 15:50:56 +01:00
]
2018-02-21 19:46:04 +01:00
2017-01-04 04:39:21 +01:00
class FieldType ( object ) :
2018-02-21 19:46:04 +01:00
def __init__ ( self , name ) :
2017-01-04 04:39:21 +01:00
self . name = name
2019-04-03 04:30:48 +02:00
def is_var_int ( self ) :
return self . name == ' var_int '
2017-01-04 04:39:21 +01:00
def is_assignable ( self ) :
2019-04-03 04:30:48 +02:00
return self . name in [ ' u8 ' , ' u16 ' , ' u32 ' , ' u64 ' , ' bool ' , ' struct amount_msat ' , ' struct amount_sat ' , ' var_int ' ] or self . name . startswith ( ' enum ' )
2017-01-04 04:39:21 +01:00
2019-03-28 02:58:29 +01:00
def needs_ptr ( self ) :
return not self . is_assignable ( )
2017-03-16 05:05:23 +01:00
# We only accelerate the u8 case: it's common and trivial.
def has_array_helper ( self ) :
return self . name in [ ' u8 ' ]
2019-03-20 19:49:25 +01:00
def base ( self ) :
basetype = self . name
if basetype . startswith ( ' struct ' ) :
basetype = basetype [ 7 : ]
elif basetype . startswith ( ' enum ' ) :
basetype = basetype [ 5 : ]
2019-04-03 04:30:48 +02:00
elif self . name == ' var_int ' :
return ' u64 '
2019-03-20 19:49:25 +01:00
return basetype
2019-03-28 02:58:29 +01:00
def is_subtype ( self ) :
for subtype in subtypes :
if subtype . name == self . base ( ) :
return True
return False
2017-01-06 03:54:45 +01:00
# Returns base size
2017-01-04 04:39:21 +01:00
@staticmethod
def _typesize ( typename ) :
2017-02-28 23:07:38 +01:00
if typename in type2size :
return type2size [ typename ]
2017-06-20 07:56:03 +02:00
elif typename . startswith ( ' struct ' ) or typename . startswith ( ' enum ' ) :
2017-12-07 23:59:39 +01:00
# We allow unknown structures/enums, for extensibility (can only happen
2017-01-06 03:54:45 +01:00
# if explicitly specified in csv)
2017-02-28 23:07:38 +01:00
return 0
else :
2017-01-04 04:39:21 +01:00
raise ValueError ( ' Unknown typename {} ' . format ( typename ) )
2018-02-22 12:59:25 +01:00
2017-02-28 23:07:38 +01:00
# Full (message, fieldname)-mappings
typemap = {
( ' update_fail_htlc ' , ' reason ' ) : FieldType ( ' u8 ' ) ,
( ' node_announcement ' , ' alias ' ) : FieldType ( ' u8 ' ) ,
( ' update_add_htlc ' , ' onion_routing_packet ' ) : FieldType ( ' u8 ' ) ,
2017-03-29 12:56:15 +02:00
( ' update_fulfill_htlc ' , ' payment_preimage ' ) : FieldType ( ' struct preimage ' ) ,
2017-02-28 23:07:38 +01:00
( ' error ' , ' data ' ) : FieldType ( ' u8 ' ) ,
( ' shutdown ' , ' scriptpubkey ' ) : FieldType ( ' u8 ' ) ,
( ' node_announcement ' , ' rgb_color ' ) : FieldType ( ' u8 ' ) ,
( ' node_announcement ' , ' addresses ' ) : FieldType ( ' u8 ' ) ,
( ' node_announcement ' , ' ipv6 ' ) : FieldType ( ' struct ipv6 ' ) ,
( ' announcement_signatures ' , ' short_channel_id ' ) : FieldType ( ' struct short_channel_id ' ) ,
( ' channel_announcement ' , ' short_channel_id ' ) : FieldType ( ' struct short_channel_id ' ) ,
2018-07-09 13:17:58 +02:00
( ' channel_update ' , ' short_channel_id ' ) : FieldType ( ' struct short_channel_id ' ) ,
2018-08-17 06:16:33 +02:00
( ' revoke_and_ack ' , ' per_commitment_secret ' ) : FieldType ( ' struct secret ' ) ,
2019-02-21 04:45:55 +01:00
( ' channel_reestablish_option_data_loss_protect ' , ' your_last_per_commitment_secret ' ) : FieldType ( ' struct secret ' ) ,
( ' channel_update ' , ' fee_base_msat ' ) : FieldType ( ' u32 ' ) ,
( ' final_incorrect_htlc_amount ' , ' incoming_htlc_amt ' ) : FieldType ( ' struct amount_msat ' ) ,
2017-02-28 23:07:38 +01:00
}
# Partial names that map to a datatype
partialtypemap = {
' signature ' : FieldType ( ' secp256k1_ecdsa_signature ' ) ,
' features ' : FieldType ( ' u8 ' ) ,
' channel_id ' : FieldType ( ' struct channel_id ' ) ,
2017-12-18 07:44:10 +01:00
' chain_hash ' : FieldType ( ' struct bitcoin_blkid ' ) ,
2017-12-18 07:41:52 +01:00
' funding_txid ' : FieldType ( ' struct bitcoin_txid ' ) ,
2017-02-28 23:07:38 +01:00
' pad ' : FieldType ( ' pad ' ) ,
2019-02-21 04:45:55 +01:00
' msat ' : FieldType ( ' struct amount_msat ' ) ,
' satoshis ' : FieldType ( ' struct amount_sat ' ) ,
2019-04-08 11:58:44 +02:00
' node_id ' : FieldType ( ' struct node_id ' ) ,
2017-02-28 23:07:38 +01:00
}
# Size to typename match
sizetypemap = {
33 : FieldType ( ' struct pubkey ' ) ,
32 : FieldType ( ' struct sha256 ' ) ,
8 : FieldType ( ' u64 ' ) ,
4 : FieldType ( ' u32 ' ) ,
2 : FieldType ( ' u16 ' ) ,
1 : FieldType ( ' u8 ' )
}
2018-02-21 19:46:04 +01:00
2017-03-29 13:00:15 +02:00
# It would be nicer if we had put '*u8' in spec and disallowed bare lenvar.
# In practice we only recognize lenvar when it's the previous field.
2017-03-16 05:05:25 +01:00
# size := baresize | arraysize
# baresize := simplesize | lenvar
# simplesize := number | type
2017-03-29 13:00:15 +02:00
# arraysize := length '*' type
# length := lenvar | number
2016-11-29 20:51:50 +01:00
class Field ( object ) :
2019-03-09 02:50:52 +01:00
def __init__ ( self , message , name , size , comments , prevname , includes ) :
2016-11-29 20:51:50 +01:00
self . message = message
2017-01-04 04:39:21 +01:00
self . comments = comments
2017-05-11 13:51:19 +02:00
self . name = name
2017-01-04 04:39:20 +01:00
self . is_len_var = False
2017-01-04 04:39:21 +01:00
self . lenvar = None
2017-03-16 05:05:25 +01:00
self . num_elems = 1
2018-07-09 08:50:31 +02:00
self . optional = False
2019-04-03 04:28:44 +02:00
self . is_tlv = False
2019-03-28 02:58:29 +01:00
self . is_subtype = False
2019-04-03 04:28:44 +02:00
2019-04-03 04:29:48 +02:00
if name . endswith ( ' _tlv ' ) :
2019-04-03 04:28:44 +02:00
self . is_tlv = True
if self . name not in tlv_fields :
2019-04-03 04:29:48 +02:00
tlv_fields [ self . name ] = [ ]
2017-03-16 05:05:25 +01:00
2018-07-09 08:50:31 +02:00
# ? means optional field (not supported for arrays)
if size . startswith ( ' ? ' ) :
self . optional = True
size = size [ 1 : ]
2017-03-16 05:05:25 +01:00
# If it's an arraysize, swallow prefix.
2018-07-09 08:50:31 +02:00
elif ' * ' in size :
2017-03-29 13:00:15 +02:00
number = size . split ( ' * ' ) [ 0 ]
if number == prevname :
2017-05-11 13:51:19 +02:00
self . lenvar = number
2017-03-29 13:00:15 +02:00
else :
self . num_elems = int ( number )
2017-03-16 05:05:25 +01:00
size = size . split ( ' * ' ) [ 1 ]
2017-09-28 05:41:19 +02:00
elif options . bolt and size == prevname :
# Raw length field, implies u8.
self . lenvar = size
size = ' 1 '
2016-11-29 20:51:50 +01:00
2017-09-28 05:41:19 +02:00
# Bolts use just a number: Guess type based on size.
if options . bolt :
2019-03-28 02:58:29 +01:00
if size . startswith ( ' $ ' ) : # this is a subtype
2019-03-28 22:45:07 +01:00
self . fieldtype = FieldType ( ' struct {} ' . format ( name ) )
2019-03-28 02:58:29 +01:00
self . is_subtype = True
if size [ 1 : ] == prevname :
self . lenvar = size [ 1 : ]
else :
raise ValueError ( ' Expected size field for subtype field {} ' . format ( name ) )
2019-04-03 04:30:48 +02:00
else :
2019-03-28 02:58:29 +01:00
if size == ' var_int ' :
base_size = 8
self . fieldtype = FieldType ( size )
else :
base_size = int ( size )
self . fieldtype = Field . _guess_type ( message , self . name , base_size )
# There are some arrays which we have to guess, based on sizes.
tsize = FieldType . _typesize ( self . fieldtype . name )
if base_size % tsize != 0 :
raise ValueError ( ' Invalid size {} for {} . {} not a multiple of {} '
. format ( base_size ,
self . message ,
self . name ,
tsize ) )
self . num_elems = int ( base_size / tsize )
2017-09-28 05:41:19 +02:00
else :
# Real typename.
2017-03-16 05:05:25 +01:00
self . fieldtype = FieldType ( size )
2016-11-29 20:51:50 +01:00
def is_padding ( self ) :
return self . name . startswith ( ' pad ' )
# Padding is always treated as an array.
def is_array ( self ) :
return self . num_elems > 1 or self . is_padding ( )
def is_variable_size ( self ) :
2017-01-04 04:39:21 +01:00
return self . lenvar is not None
2016-11-29 20:51:50 +01:00
2018-07-09 08:50:31 +02:00
def needs_ptr_to_ptr ( self ) :
return self . is_variable_size ( ) or self . optional
2016-11-29 20:51:50 +01:00
def is_assignable ( self ) :
2018-07-09 08:50:31 +02:00
if self . is_array ( ) or self . needs_ptr_to_ptr ( ) :
2016-11-29 20:51:50 +01:00
return False
2017-01-04 04:39:21 +01:00
return self . fieldtype . is_assignable ( )
2016-11-29 20:51:50 +01:00
2017-03-16 05:05:23 +01:00
def has_array_helper ( self ) :
return self . fieldtype . has_array_helper ( )
2017-12-10 13:41:10 +01:00
2017-01-04 04:39:21 +01:00
# Returns FieldType
2016-11-29 20:51:50 +01:00
@staticmethod
2017-01-04 04:39:21 +01:00
def _guess_type ( message , fieldname , base_size ) :
2017-02-28 23:07:38 +01:00
# Check for full (message, fieldname)-matches
if ( message , fieldname ) in typemap :
return typemap [ ( message , fieldname ) ]
# Check for partial field names
for k , v in partialtypemap . items ( ) :
if k in fieldname :
return v
# Check for size matches
if base_size in sizetypemap :
return sizetypemap [ base_size ]
2017-01-04 04:39:21 +01:00
2018-02-21 19:46:04 +01:00
raise ValueError ( ' Unknown size {} for {} ' . format ( base_size , fieldname ) )
2016-11-29 20:51:50 +01:00
2018-02-22 12:59:25 +01:00
2018-02-20 21:59:09 +01:00
fromwire_impl_templ = """ bool fromwire_ {name} ( {ctx} const void *p {args} )
2017-02-28 23:07:38 +01:00
{ {
{ fields }
2018-06-19 10:38:51 +02:00
\tconst u8 * cursor = p ;
2018-07-28 08:00:16 +02:00
\tsize_t plen = tal_count ( p ) ;
2017-02-28 23:07:38 +01:00
2018-06-19 10:38:51 +02:00
\tif ( fromwire_u16 ( & cursor , & plen ) != { enum . name } )
\t \treturn false ;
2017-02-28 23:07:38 +01:00
{ subcalls }
2018-06-19 10:38:51 +02:00
\treturn cursor != NULL ;
2017-02-28 23:07:38 +01:00
} }
"""
2019-03-28 22:26:25 +01:00
fromwire_tlv_impl_templ = """ static bool fromwire_ {tlv_name} _ {name} ( {ctx} {args} )
2019-03-28 22:23:18 +01:00
{ {
2019-03-28 22:25:19 +01:00
\tsize_t start_len = * plen ;
2019-03-28 22:23:18 +01:00
{ fields }
2019-03-28 22:25:19 +01:00
\tif ( start_len < len )
2019-03-28 22:23:18 +01:00
\t \treturn false ;
{ subcalls }
2019-03-28 22:25:19 +01:00
\treturn cursor != NULL & & ( start_len - * plen == len ) ;
2019-03-28 22:23:18 +01:00
} }
"""
2019-04-09 01:43:14 +02:00
fromwire_subtype_impl_templ = """ {static} bool fromwire_ {name} ( {ctx} {args} )
2019-03-28 02:58:29 +01:00
{ {
{ fields }
{ subcalls }
\treturn cursor != NULL ;
} }
"""
2019-04-09 01:43:14 +02:00
fromwire_subtype_header_templ = """ bool fromwire_ {name} ( {ctx} {args} ); """
2018-02-20 21:59:09 +01:00
fromwire_header_templ = """ bool fromwire_ {name} ( {ctx} const void *p {args} );
2017-02-28 23:07:38 +01:00
"""
towire_header_templ = """ u8 *towire_ {name} (const tal_t *ctx {args} );
"""
towire_impl_templ = """ u8 *towire_ {name} (const tal_t *ctx {args} )
{ {
{ field_decls }
2018-06-19 10:38:51 +02:00
\tu8 * p = tal_arr ( ctx , u8 , 0 ) ;
\ttowire_u16 ( & p , { enumname } ) ;
2017-02-28 23:07:38 +01:00
{ subcalls }
2018-06-19 10:38:51 +02:00
\treturn memcheck ( p , tal_count ( p ) ) ;
2017-02-28 23:07:38 +01:00
} }
"""
2018-02-01 01:08:25 +01:00
2019-03-28 22:17:07 +01:00
towire_tlv_templ = """ u8 *towire_ {name} (const tal_t *ctx {args} )
{ {
{ field_decls }
\tu8 * p = tal_arr ( ctx , u8 , 0 ) ;
\ttowire_u16 ( & p , { enumname } ) ;
\ttowire_u16 ( & p , { len } ) ;
{ subcalls }
\treturn memcheck ( p , tal_count ( p ) ) ;
} }
"""
fromwire_tlv_templ = """ bool frowire_ {name} ( {ctx} const void *p {args} )
{ {
{ fields }
\tconst u8 * cursor = p ;
\tsize_t plen = tal_count ( p ) ;
\tif ( frmwire_u16 ( & cursor , & plen ) != { enum . name } )
\t \treturn false ;
{ subcalls }
\treturn cursor != NULL ;
} }
"""
2018-06-28 03:30:49 +02:00
printwire_header_templ = """ void printwire_ {name} (const char *fieldname, const u8 *cursor);
2018-02-01 01:08:25 +01:00
"""
2019-04-05 05:06:49 +02:00
printwire_toplevel_tmpl = """ \t size_t plen = tal_count(cursor);
2018-02-01 01:08:25 +01:00
2018-06-19 10:38:51 +02:00
\tif ( fromwire_u16 ( & cursor , & plen ) != { enum . name } ) { {
\t \tprintf ( " WRONG TYPE?! \\ n " ) ;
\t \treturn ;
2019-04-05 05:06:49 +02:00
\t } } """
2018-02-01 01:08:25 +01:00
2019-04-05 05:06:49 +02:00
printwire_impl_templ = """ {is_internal} void printwire_ {name} (const char *fieldname, const u8 * {cursor_ptr} cursor {tlv_args} )
{ {
{ toplevel_msg_setup } { subcalls } { lencheck }
} }
"""
2018-02-01 01:08:25 +01:00
2019-04-05 05:06:49 +02:00
printwire_lencheck = """
2018-06-19 10:38:51 +02:00
\tif ( plen != 0 )
\t \tprintf ( " EXTRA: %s \\ n " , tal_hexstr ( NULL , cursor , plen ) ) ;
2018-02-01 01:08:25 +01:00
"""
2018-02-21 19:46:04 +01:00
2018-07-16 05:12:44 +02:00
class CCode ( object ) :
""" Simple class to create indented C code """
def __init__ ( self ) :
self . indent = 1
self . single_indent = False
self . code = [ ]
def append ( self , lines ) :
for line in lines . split ( ' \n ' ) :
# Let us to the indenting please!
assert ' \t ' not in line
# Special case: } by itself is pre-unindented.
if line == ' } ' :
self . indent - = 1
self . code . append ( " \t " * self . indent + line )
continue
self . code . append ( " \t " * self . indent + line )
if self . single_indent :
self . indent - = 1
self . single_indent = False
if line . endswith ( ' { ' ) :
self . indent + = 1
elif line . endswith ( ' } ' ) :
self . indent - = 1
elif line . startswith ( ' for ' ) or line . startswith ( ' if ' ) :
self . indent + = 1
self . single_indent = True
def __str__ ( self ) :
assert self . indent == 1
assert not self . single_indent
return ' \n ' . join ( self . code )
2016-11-29 20:51:50 +01:00
class Message ( object ) :
2019-03-27 23:39:58 +01:00
def __init__ ( self , name , enum , comments , is_tlv = False ) :
2016-11-29 20:51:50 +01:00
self . name = name
self . enum = enum
2017-01-04 04:39:21 +01:00
self . comments = comments
2016-11-29 20:51:50 +01:00
self . fields = [ ]
2017-01-04 04:39:20 +01:00
self . has_variable_fields = False
2019-03-28 22:18:16 +01:00
self . is_tlv = is_tlv
2016-11-29 20:51:50 +01:00
2017-02-28 23:07:38 +01:00
def checkLenField ( self , field ) :
2018-07-09 08:50:31 +02:00
# Optional fields don't have a len.
2018-07-16 03:43:44 +02:00
if field . optional :
2018-07-09 08:50:31 +02:00
return
2016-11-29 20:51:50 +01:00
for f in self . fields :
if f . name == field . lenvar :
2019-04-03 04:30:48 +02:00
if not ( f . fieldtype . name == ' u16 ' or f . fieldtype . name == ' var_int ' ) and options . bolt :
raise ValueError ( ' Field {} has non-u16 and non-var_int length variable {} (type {} ) '
2017-09-28 05:41:19 +02:00
. format ( field . name , field . lenvar , f . fieldtype . name ) )
2016-11-29 20:51:50 +01:00
2018-07-09 08:50:31 +02:00
if f . is_array ( ) or f . needs_ptr_to_ptr ( ) :
2016-11-29 20:51:50 +01:00
raise ValueError ( ' Field {} has non-simple length variable {} '
. format ( field . name , field . lenvar ) )
2018-02-21 19:46:04 +01:00
f . is_len_var = True
2017-01-10 05:49:25 +01:00
f . lenvar_for = field
2016-11-29 20:51:50 +01:00
return
raise ValueError ( ' Field {} unknown length variable {} '
. format ( field . name , field . lenvar ) )
2018-02-21 19:46:04 +01:00
def addField ( self , field ) :
2016-11-29 20:51:50 +01:00
# We assume field lengths are 16 bit, to avoid overflow issues and
# massive allocations.
if field . is_variable_size ( ) :
self . checkLenField ( field )
2017-01-04 04:39:20 +01:00
self . has_variable_fields = True
2019-03-20 19:49:25 +01:00
elif field . fieldtype . base ( ) in varlen_structs or field . optional :
2018-07-09 08:50:31 +02:00
self . has_variable_fields = True
2016-11-29 20:51:50 +01:00
self . fields . append ( field )
2019-03-28 22:25:19 +01:00
def print_fromwire_array ( self , ctx , subcalls , basetype , f , name , num_elems , is_tlv = False ) :
p_ref = ' ' if is_tlv else ' & '
2017-03-16 05:05:23 +01:00
if f . has_array_helper ( ) :
2019-03-28 22:28:56 +01:00
subcalls . append ( ' fromwire_ {} _array( {} cursor, {} plen, {} , {} ); '
. format ( basetype , p_ref , p_ref , name , num_elems ) )
2017-03-16 05:05:23 +01:00
else :
2018-07-16 05:12:44 +02:00
subcalls . append ( ' for (size_t i = 0; i < {} ; i++) '
2017-03-16 05:05:23 +01:00
. format ( num_elems ) )
2017-06-06 05:03:01 +02:00
if f . fieldtype . is_assignable ( ) :
2019-03-28 22:28:56 +01:00
subcalls . append ( ' ( {} )[i] = fromwire_ {} ( {} cursor, {} plen); '
. format ( name , basetype , p_ref , p_ref ) )
2018-02-08 02:23:46 +01:00
elif basetype in varlen_structs :
2019-03-28 22:28:56 +01:00
subcalls . append ( ' ( {} )[i] = fromwire_ {} ( {} , {} cursor, {} plen); '
. format ( name , basetype , ctx , p_ref , p_ref ) )
2017-03-16 05:05:23 +01:00
else :
2019-03-28 02:58:29 +01:00
ctx_arg = ctx + ' , ' if f . fieldtype . is_subtype ( ) else ' '
subcalls . append ( ' fromwire_ {} ( {} {} cursor, {} plen, {} + i); '
. format ( basetype , ctx_arg , p_ref , p_ref , name ) )
2017-03-16 05:05:23 +01:00
2019-03-27 23:58:02 +01:00
def print_fromwire ( self , is_header ) :
2017-02-28 23:07:38 +01:00
ctx_arg = ' const tal_t *ctx, ' if self . has_variable_fields else ' '
2016-11-29 20:51:50 +01:00
2017-02-28 23:07:38 +01:00
args = [ ]
2017-12-10 13:41:10 +01:00
2016-11-29 20:51:50 +01:00
for f in self . fields :
2017-02-28 23:07:38 +01:00
if f . is_len_var or f . is_padding ( ) :
2017-01-04 04:39:20 +01:00
continue
2017-02-28 23:07:38 +01:00
elif f . is_array ( ) :
args . append ( ' , {} {} [ {} ] ' . format ( f . fieldtype . name , f . name , f . num_elems ) )
2019-03-28 22:23:18 +01:00
elif f . is_tlv :
2019-03-28 22:26:25 +01:00
args . append ( ' , struct {} * {} ' . format ( f . name , f . name ) )
2016-11-29 20:51:50 +01:00
else :
2018-02-21 19:46:04 +01:00
ptrs = ' * '
2018-02-08 02:24:46 +01:00
# If we're handing a variable array, we need a ptr-to-ptr.
2018-07-09 08:50:31 +02:00
if f . needs_ptr_to_ptr ( ) :
2018-02-08 02:24:46 +01:00
ptrs + = ' * '
# If each type is a variable length, we need a ptr to that.
2019-03-20 19:49:25 +01:00
if f . fieldtype . base ( ) in varlen_structs :
2018-02-08 02:24:46 +01:00
ptrs + = ' * '
args . append ( ' , {} {} {} ' . format ( f . fieldtype . name , ptrs , f . name ) )
2017-01-04 04:39:20 +01:00
2017-02-28 23:07:38 +01:00
template = fromwire_header_templ if is_header else fromwire_impl_templ
2019-04-03 04:30:48 +02:00
fields = [ ' \t {} {} ; \n ' . format ( f . fieldtype . base ( ) , f . name ) for f in self . fields if f . is_len_var ]
2016-11-29 20:51:50 +01:00
2018-07-16 05:12:44 +02:00
subcalls = CCode ( )
2016-11-29 20:51:50 +01:00
for f in self . fields :
2019-03-20 19:49:25 +01:00
basetype = f . fieldtype . base ( )
2019-04-03 04:30:48 +02:00
if f . fieldtype . is_var_int ( ) :
basetype = ' var_int '
2016-11-29 20:51:50 +01:00
2017-01-04 04:39:21 +01:00
for c in f . comments :
2018-07-16 05:12:44 +02:00
subcalls . append ( ' /* {} */ ' . format ( c ) )
2017-01-04 04:39:21 +01:00
2017-02-21 05:45:19 +01:00
if f . is_padding ( ) :
2018-07-16 05:12:44 +02:00
subcalls . append ( ' fromwire_pad(&cursor, &plen, {} ); '
2017-02-28 23:07:38 +01:00
. format ( f . num_elems ) )
2017-01-04 04:39:20 +01:00
elif f . is_array ( ) :
2018-11-21 22:44:37 +01:00
self . print_fromwire_array ( ' ctx ' , subcalls , basetype , f , f . name ,
2017-03-16 05:05:23 +01:00
f . num_elems )
2019-03-28 22:23:18 +01:00
elif f . is_tlv :
if not f . is_variable_size ( ) :
raise TypeError ( ' TLV {} not variable size ' . format ( f . name ) )
2019-03-28 22:28:08 +01:00
subcalls . append ( ' struct {tlv_name} *_tlv = fromwire__ {tlv_name} (ctx, &cursor, &plen, & {tlv_len} ); '
2019-03-28 22:23:18 +01:00
. format ( tlv_name = f . name , tlv_len = f . lenvar ) )
2019-03-28 22:28:08 +01:00
subcalls . append ( ' if (!_tlv) ' )
2019-03-28 22:23:18 +01:00
subcalls . append ( ' return false; ' )
2019-03-28 22:28:08 +01:00
subcalls . append ( ' * {tlv_name} = *_tlv; ' . format ( tlv_name = f . name ) )
2016-11-29 20:51:50 +01:00
elif f . is_variable_size ( ) :
2018-07-16 05:12:44 +02:00
subcalls . append ( " //2nd case {name} " . format ( name = f . name ) )
2018-02-08 02:24:46 +01:00
typename = f . fieldtype . name
# If structs are varlen, need array of ptrs to them.
if basetype in varlen_structs :
typename + = ' * '
2018-07-16 05:12:44 +02:00
subcalls . append ( ' * {} = {} ? tal_arr(ctx, {} , {} ) : NULL; '
2018-02-08 02:24:46 +01:00
. format ( f . name , f . lenvar , typename , f . lenvar ) )
2017-03-16 05:05:23 +01:00
2018-11-21 22:44:37 +01:00
# Allocate these off the array itself, if they need alloc.
self . print_fromwire_array ( ' * ' + f . name , subcalls , basetype , f ,
' * ' + f . name , f . lenvar )
2018-07-09 08:50:31 +02:00
else :
2019-01-15 04:58:27 +01:00
if f . optional :
assignable = f . fieldtype . is_assignable ( )
deref = ' * '
else :
deref = ' '
assignable = f . is_assignable ( )
if assignable :
if f . is_len_var :
s = ' {} = fromwire_ {} (&cursor, &plen); ' . format ( f . name , basetype )
else :
s = ' {} * {} = fromwire_ {} (&cursor, &plen); ' . format ( deref , f . name , basetype )
elif basetype in varlen_structs :
s = ' {} * {} = fromwire_ {} (ctx, &cursor, &plen); ' . format ( deref , f . name , basetype )
else :
s = ' fromwire_ {} (&cursor, &plen, {} {} ); ' . format ( basetype , deref , f . name )
2018-07-16 03:43:44 +02:00
if f . optional :
2018-07-16 05:12:44 +02:00
subcalls . append ( " if (!fromwire_bool(&cursor, &plen)) \n "
" * {} = NULL; \n "
" else {{ \n "
" * {} = tal(ctx, {} ); \n "
2019-01-15 04:58:27 +01:00
" {} \n "
2018-07-16 05:12:44 +02:00
" }} "
2018-07-09 08:50:31 +02:00
. format ( f . name , f . name , f . fieldtype . name ,
2019-01-15 04:58:27 +01:00
s ) )
2017-01-04 04:39:20 +01:00
else :
2019-01-15 04:58:27 +01:00
subcalls . append ( s )
2017-02-28 23:07:38 +01:00
return template . format (
name = self . name ,
ctx = ctx_arg ,
args = ' ' . join ( args ) ,
fields = ' ' . join ( fields ) ,
enum = self . enum ,
2018-07-16 05:12:44 +02:00
subcalls = str ( subcalls )
2017-02-28 23:07:38 +01:00
)
2017-01-04 04:39:20 +01:00
2019-03-28 22:23:18 +01:00
def print_towire_array ( self , subcalls , basetype , f , num_elems , is_tlv = False ) :
p_ref = ' ' if is_tlv else ' & '
msg_name = self . name + ' -> ' if is_tlv else ' '
2017-03-16 05:05:23 +01:00
if f . has_array_helper ( ) :
2019-03-28 22:23:18 +01:00
subcalls . append ( ' towire_ {} _array( {} p, {} {} , {} ); '
. format ( basetype , p_ref , msg_name , f . name , num_elems ) )
2017-03-16 05:05:23 +01:00
else :
2018-07-16 05:12:44 +02:00
subcalls . append ( ' for (size_t i = 0; i < {} ; i++) '
2017-06-06 05:03:01 +02:00
. format ( num_elems ) )
2018-02-08 02:24:46 +01:00
if f . fieldtype . is_assignable ( ) or basetype in varlen_structs :
2019-03-28 22:23:18 +01:00
subcalls . append ( ' towire_ {} ( {} p, {} {} [i]); '
. format ( basetype , p_ref , msg_name , f . name ) )
2017-06-06 05:03:01 +02:00
else :
2019-03-28 22:23:18 +01:00
subcalls . append ( ' towire_ {} ( {} p, {} {} + i); '
. format ( basetype , p_ref , msg_name , f . name ) )
2017-03-16 05:05:23 +01:00
2019-03-28 22:25:19 +01:00
def find_tlv_lenvar_field ( self , tlv_name ) :
return [ f for f in self . fields if f . is_len_var and f . lenvar_for . is_tlv and f . lenvar_for . name == tlv_name ] [ 0 ]
2019-03-27 23:58:02 +01:00
def print_towire ( self , is_header ) :
2017-02-28 23:07:38 +01:00
template = towire_header_templ if is_header else towire_impl_templ
args = [ ]
2017-01-04 04:39:20 +01:00
for f in self . fields :
2017-01-10 05:49:25 +01:00
if f . is_padding ( ) or f . is_len_var :
2017-01-04 04:39:20 +01:00
continue
if f . is_array ( ) :
2017-02-28 23:07:38 +01:00
args . append ( ' , const {} {} [ {} ] ' . format ( f . fieldtype . name , f . name , f . num_elems ) )
2019-03-28 22:23:18 +01:00
elif f . is_tlv :
2019-03-28 22:26:25 +01:00
args . append ( ' , const struct {} * {} ' . format ( f . name , f . name ) )
2017-01-04 04:39:20 +01:00
elif f . is_assignable ( ) :
2017-02-28 23:07:38 +01:00
args . append ( ' , {} {} ' . format ( f . fieldtype . name , f . name ) )
2019-03-20 19:49:25 +01:00
elif f . is_variable_size ( ) and f . fieldtype . base ( ) in varlen_structs :
2018-02-08 02:24:46 +01:00
args . append ( ' , const {} ** {} ' . format ( f . fieldtype . name , f . name ) )
2017-01-04 04:39:20 +01:00
else :
2017-02-28 23:07:38 +01:00
args . append ( ' , const {} * {} ' . format ( f . fieldtype . name , f . name ) )
2016-11-29 20:51:50 +01:00
2017-02-28 23:07:38 +01:00
field_decls = [ ]
2017-01-10 05:49:25 +01:00
for f in self . fields :
if f . is_len_var :
2019-03-28 22:23:18 +01:00
if f . lenvar_for . is_tlv :
2019-03-28 22:25:19 +01:00
# used below...
2019-04-03 04:30:48 +02:00
field_decls . append ( ' \t {0} {1} ; ' . format ( f . fieldtype . base ( ) , f . name ) )
2019-03-28 22:23:18 +01:00
else :
field_decls . append ( ' \t {0} {1} = tal_count( {2} ); ' . format (
f . fieldtype . name , f . name , f . lenvar_for . name
) )
2016-11-29 20:51:50 +01:00
2018-07-16 05:12:44 +02:00
subcalls = CCode ( )
2016-11-29 20:51:50 +01:00
for f in self . fields :
2019-03-20 19:49:25 +01:00
basetype = f . fieldtype . base ( )
2016-11-29 20:51:50 +01:00
2017-01-04 04:39:21 +01:00
for c in f . comments :
2018-07-16 05:12:44 +02:00
subcalls . append ( ' /* {} */ ' . format ( c ) )
2017-01-04 04:39:21 +01:00
2017-01-04 04:39:20 +01:00
if f . is_padding ( ) :
2018-07-16 05:12:44 +02:00
subcalls . append ( ' towire_pad(&p, {} ); '
2018-02-21 19:46:04 +01:00
. format ( f . num_elems ) )
2017-01-04 04:39:20 +01:00
elif f . is_array ( ) :
2017-03-16 05:05:23 +01:00
self . print_towire_array ( subcalls , basetype , f , f . num_elems )
2019-03-28 22:25:19 +01:00
elif f . is_len_var and f . lenvar_for . is_tlv :
continue # taken care of below
2019-03-28 22:23:18 +01:00
elif f . is_tlv :
if not f . is_variable_size ( ) :
2019-03-28 22:25:19 +01:00
raise ValueError ( ' TLV {} not variable size ' . format ( f . name ) )
lenvar_field = self . find_tlv_lenvar_field ( f . name )
subcalls . append ( ' /* ~~build TLV for {} ~~*/ ' . format ( f . name ) )
subcalls . append ( " u8 * {tlv_name} _buffer = tal_arr(ctx, u8, 0); \n "
" towire__ {tlv_name} (ctx, & {tlv_name} _buffer, {tlv_name} ); \n "
" {lenvar_field} = tal_count( {tlv_name} _buffer); \n "
" towire_ {lenvar_fieldtype} (&p, {lenvar_field} ); \n "
" towire_u8_array(&p, {tlv_name} _buffer, {lenvar_field} ); \n " . format (
tlv_name = f . name ,
lenvar_field = lenvar_field . name ,
lenvar_fieldtype = lenvar_field . fieldtype . name ) )
2016-11-29 20:51:50 +01:00
elif f . is_variable_size ( ) :
2017-03-16 05:05:23 +01:00
self . print_towire_array ( subcalls , basetype , f , f . lenvar )
2016-11-29 20:51:50 +01:00
else :
2018-07-16 03:43:44 +02:00
if f . optional :
2019-01-15 04:58:27 +01:00
if f . fieldtype . is_assignable ( ) :
deref = ' * '
else :
deref = ' '
2018-07-16 05:12:44 +02:00
subcalls . append ( " if (! {} ) \n "
" towire_bool(&p, false); \n "
" else {{ \n "
" towire_bool(&p, true); \n "
2019-01-15 04:58:27 +01:00
" towire_ {} (&p, {} {} ); \n "
" }} " . format ( f . name , basetype , deref , f . name ) )
2018-07-09 08:50:31 +02:00
else :
2018-07-16 05:12:44 +02:00
subcalls . append ( ' towire_ {} (&p, {} ); '
2018-07-09 08:50:31 +02:00
. format ( basetype , f . name ) )
2016-11-29 20:51:50 +01:00
2017-02-28 23:07:38 +01:00
return template . format (
name = self . name ,
args = ' ' . join ( args ) ,
enumname = self . enum . name ,
field_decls = ' \n ' . join ( field_decls ) ,
2018-07-16 05:12:44 +02:00
subcalls = str ( subcalls ) ,
2017-02-28 23:07:38 +01:00
)
2019-04-05 05:06:49 +02:00
def add_truncate_check ( self , subcalls , ref ) :
2018-02-01 01:08:25 +01:00
# Report if truncated, otherwise print.
2019-04-05 05:06:49 +02:00
call = ' if (! {} cursor) {{ \n printf( " **TRUNCATED** \\ n " ); \n return; \n }} ' . format ( ref )
subcalls . append ( call )
2018-02-01 01:08:25 +01:00
2019-04-05 05:06:49 +02:00
def print_printwire_array ( self , subcalls , basetype , f , num_elems , ref ) :
truncate_check_ref = ' ' if ref else ' * '
2018-02-01 01:08:25 +01:00
if f . has_array_helper ( ) :
2019-04-05 05:06:49 +02:00
subcalls . append ( ' printwire_ {} _array(tal_fmt(NULL, " %s . {} " , fieldname), {} cursor, {} plen, {} ); '
. format ( basetype , f . name , ref , ref , num_elems ) )
2018-02-01 01:08:25 +01:00
else :
2018-07-16 05:12:44 +02:00
subcalls . append ( ' printf( " [ " ); ' )
subcalls . append ( ' for (size_t i = 0; i < {} ; i++) {{ '
2018-02-01 01:08:25 +01:00
. format ( num_elems ) )
2018-07-16 05:12:44 +02:00
subcalls . append ( ' {} v; ' . format ( f . fieldtype . name ) )
2018-02-01 01:08:25 +01:00
if f . fieldtype . is_assignable ( ) :
2019-04-05 05:06:49 +02:00
subcalls . append ( ' v = fromwire_ {} ( {} cursor, {} plen); '
. format ( f . fieldtype . name , basetype , ref , ref ) )
2018-02-01 01:08:25 +01:00
else :
# We don't handle this yet!
2018-02-21 19:46:04 +01:00
assert ( basetype not in varlen_structs )
2018-02-01 01:08:25 +01:00
2019-04-05 05:06:49 +02:00
subcalls . append ( ' fromwire_ {} ( {} cursor, {} plen, &v); '
. format ( basetype , ref , ref ) )
2018-02-01 01:08:25 +01:00
2019-04-05 05:06:49 +02:00
self . add_truncate_check ( subcalls , truncate_check_ref )
2018-02-01 01:08:25 +01:00
2018-07-16 05:12:44 +02:00
subcalls . append ( ' printwire_ {} (tal_fmt(NULL, " %s . {} " , fieldname), &v); '
2018-06-28 03:30:49 +02:00
. format ( basetype , f . name ) )
2018-07-16 05:12:44 +02:00
subcalls . append ( ' } ' )
subcalls . append ( ' printf( " ] " ); ' )
2018-02-01 01:08:25 +01:00
2019-04-05 05:06:49 +02:00
def print_printwire ( self , is_header , is_tlv = False ) :
2018-02-01 01:08:25 +01:00
template = printwire_header_templ if is_header else printwire_impl_templ
fields = [ ' \t {} {} ; \n ' . format ( f . fieldtype . name , f . name ) for f in self . fields if f . is_len_var ]
2019-04-05 05:06:49 +02:00
tlv_args = ' ' if not is_tlv else ' , size_t *plen '
ref = ' & ' if not is_tlv else ' '
truncate_check_ref = ' ' if not is_tlv else ' * '
toplevel_msg_setup = ' '
if not is_tlv :
toplevel_msg_setup = printwire_toplevel_tmpl . format ( enum = self . enum )
2018-07-16 05:12:44 +02:00
subcalls = CCode ( )
2018-02-01 01:08:25 +01:00
for f in self . fields :
2019-03-20 19:49:25 +01:00
basetype = f . fieldtype . base ( )
2018-02-01 01:08:25 +01:00
for c in f . comments :
2018-07-16 05:12:44 +02:00
subcalls . append ( ' /* {} */ ' . format ( c ) )
2018-02-01 01:08:25 +01:00
if f . is_len_var :
2019-04-05 05:06:49 +02:00
if f . fieldtype . is_var_int ( ) :
subcalls . append ( ' {} {} = fromwire_ {} ( {} cursor, {} plen); '
. format ( basetype , f . name , ' var_int ' , ref , ref ) )
else :
subcalls . append ( ' {} {} = fromwire_ {} ( {} cursor, {} plen); '
. format ( f . fieldtype . name , f . name , basetype , ref , ref ) )
self . add_truncate_check ( subcalls , truncate_check_ref )
2018-02-01 01:08:25 +01:00
continue
2018-07-16 05:12:44 +02:00
subcalls . append ( ' printf( " {} = " ); ' . format ( f . name ) )
2018-02-01 01:08:25 +01:00
if f . is_padding ( ) :
2019-04-05 05:06:49 +02:00
subcalls . append ( ' printwire_pad(tal_fmt(NULL, " %s . {} " , fieldname), {} cursor, {} plen, {} ); '
. format ( f . name , ref , ref , f . num_elems ) )
self . add_truncate_check ( subcalls , truncate_check_ref )
2018-02-01 01:08:25 +01:00
elif f . is_array ( ) :
2019-04-05 05:06:49 +02:00
self . print_printwire_array ( subcalls , basetype , f , f . num_elems , ref )
self . add_truncate_check ( subcalls , truncate_check_ref )
2018-02-01 01:08:25 +01:00
elif f . is_variable_size ( ) :
2019-04-05 05:06:49 +02:00
self . print_printwire_array ( subcalls , basetype , f , f . lenvar , ref )
self . add_truncate_check ( subcalls , truncate_check_ref )
2018-02-01 01:08:25 +01:00
else :
2018-07-16 03:43:44 +02:00
if f . optional :
2019-04-05 05:06:49 +02:00
subcalls . append ( " if (fromwire_bool( {} cursor, {} plen)) { " . format ( ref , ref ) )
2018-07-09 08:50:31 +02:00
2018-02-01 01:08:25 +01:00
if f . is_assignable ( ) :
2019-04-05 05:06:49 +02:00
subcalls . append ( ' {} {} = fromwire_ {} ( {} cursor, {} plen); '
. format ( f . fieldtype . name , f . name , basetype , ref , ref ) )
2018-02-01 01:08:25 +01:00
else :
# Don't handle these yet.
2018-02-21 19:46:04 +01:00
assert ( basetype not in varlen_structs )
2018-07-16 05:12:44 +02:00
subcalls . append ( ' {} {} ; ' .
2018-02-21 19:46:04 +01:00
format ( f . fieldtype . name , f . name ) )
2019-04-05 05:06:49 +02:00
subcalls . append ( ' fromwire_ {} ( {} cursor, {} plen, & {} ); '
. format ( basetype , ref , ref , f . name ) )
2018-02-01 01:08:25 +01:00
2019-04-05 05:06:49 +02:00
self . add_truncate_check ( subcalls , truncate_check_ref )
2018-07-16 05:12:44 +02:00
subcalls . append ( ' printwire_ {} (tal_fmt(NULL, " %s . {} " , fieldname), & {} ); '
2018-06-28 03:30:49 +02:00
. format ( basetype , f . name , f . name ) )
2018-07-16 03:43:44 +02:00
if f . optional :
2018-07-16 05:12:44 +02:00
subcalls . append ( " } else { " )
2019-04-05 05:06:49 +02:00
self . add_truncate_check ( subcalls , truncate_check_ref )
2018-07-16 05:12:44 +02:00
subcalls . append ( " } " )
2018-02-01 01:08:25 +01:00
2019-04-05 05:06:49 +02:00
len_check = ' ' if is_tlv else printwire_lencheck
2018-02-01 01:08:25 +01:00
return template . format (
2019-04-05 05:06:49 +02:00
tlv_args = tlv_args ,
2018-02-01 01:08:25 +01:00
name = self . name ,
fields = ' ' . join ( fields ) ,
2019-04-05 05:06:49 +02:00
toplevel_msg_setup = toplevel_msg_setup ,
subcalls = str ( subcalls ) ,
lencheck = len_check ,
cursor_ptr = ( ' ' if not is_tlv else ' * ' ) ,
is_internal = ( ' ' if not is_tlv else ' static ' )
2018-02-01 01:08:25 +01:00
)
2019-03-27 23:39:58 +01:00
class TlvMessage ( Message ) :
def __init__ ( self , name , enum , comments ) :
super ( ) . __init__ ( name , enum , comments , is_tlv = True )
2019-03-28 22:19:12 +01:00
def print_struct ( self ) :
2019-03-27 23:39:58 +01:00
return TlvMessage . _inner_print_struct ( ' tlv_msg_ ' + self . name , self . fields )
@staticmethod
def _inner_print_struct ( struct_name , fields ) :
2019-03-28 22:19:12 +01:00
""" returns a string representation of this message as
a struct """
fmt_fields = CCode ( )
2019-03-27 23:39:58 +01:00
for f in fields :
2019-03-28 22:19:12 +01:00
if f . is_len_var or f . is_padding ( ) :
2019-03-27 23:39:58 +01:00
# there is no ethical padding under structs
2019-03-28 22:19:12 +01:00
continue
elif f . is_variable_size ( ) :
fmt_fields . append ( ' {} * {} ; ' . format ( f . fieldtype . name , f . name ) )
elif f . is_array ( ) :
fmt_fields . append ( ' {} {} [ {} ]; ' . format ( f . fieldtype . name , f . name , f . num_elems ) )
else :
fmt_fields . append ( ' {} {} ; ' . format ( f . fieldtype . name , f . name ) )
2019-03-27 23:39:58 +01:00
return """
struct { struct_name } { {
{ fields }
} } ;
""" .format(
struct_name = struct_name ,
2019-03-28 22:19:12 +01:00
fields = str ( fmt_fields ) )
2019-03-27 23:48:45 +01:00
def print_towire ( self , is_header , tlv_name ) :
2019-03-27 23:58:02 +01:00
""" prints towire function definition for a TLV message. """
2019-03-27 23:48:45 +01:00
if is_header :
return ' '
field_decls = [ ]
for f in self . fields :
if f . is_tlv :
raise TypeError ( " Nested TLVs aren ' t allowed!! {} -> {} " . format ( tlv_name , f . name ) )
elif f . optional :
raise TypeError ( " Optional fields on TLV messages not currently supported. {} -> {} " . format ( tlv_name , f . name ) )
if f . is_len_var :
field_decls . append ( ' \t {0} {1} = tal_count( {2} -> {3} ); ' . format (
f . fieldtype . name , f . name , self . name , f . lenvar_for . name
) )
subcalls = CCode ( )
for f in self . fields :
basetype = f . fieldtype . base ( )
for c in f . comments :
subcalls . append ( ' /* {} */ ' . format ( c ) )
if f . is_padding ( ) :
subcalls . append ( ' towire_pad(p, {} ); ' . format ( f . num_elems ) )
elif f . is_array ( ) :
self . print_towire_array ( subcalls , basetype , f , f . num_elems ,
is_tlv = True )
elif f . is_variable_size ( ) :
self . print_towire_array ( subcalls , basetype , f , f . lenvar ,
is_tlv = True )
elif f . is_len_var :
subcalls . append ( ' towire_ {} (p, {} ); ' . format ( basetype , f . name ) )
else :
subcalls . append ( ' towire_ {} (p, {} -> {} ); ' . format ( basetype , self . name , f . name ) )
return tlv_message_towire_stub . format (
tlv_name = tlv_name ,
name = self . name ,
field_decls = ' \n ' . join ( field_decls ) ,
subcalls = str ( subcalls ) )
def print_fromwire ( self , is_header , tlv_name ) :
""" prints fromwire function definition for a TLV message.
these are significantly different in that they take in a struct
to populate , instead of fields , as well as a length to read in
"""
if is_header :
return ' '
ctx_arg = ' const tal_t *ctx, ' if self . has_variable_fields else ' '
args = ' const u8 **cursor, size_t *plen, const u16 len, struct tlv_msg_ {name} * {name} ' . format ( name = self . name )
fields = [ ' \t {} {} ; \n ' . format ( f . fieldtype . name , f . name ) for f in self . fields if f . is_len_var ]
subcalls = CCode ( )
for f in self . fields :
basetype = f . fieldtype . base ( )
if f . is_tlv :
raise TypeError ( ' Nested TLVs arent allowed!! ' )
elif f . optional :
raise TypeError ( ' Optional fields on TLV messages not currently supported ' )
for c in f . comments :
subcalls . append ( ' /* {} */ ' . format ( c ) )
if f . is_padding ( ) :
subcalls . append ( ' fromwire_pad(cursor, plen, {} ); '
. format ( f . num_elems ) )
elif f . is_array ( ) :
name = ' * {} -> {} ' . format ( self . name , f . name )
self . print_fromwire_array ( ' ctx ' , subcalls , basetype , f , name ,
f . num_elems , is_tlv = True )
elif f . is_variable_size ( ) :
subcalls . append ( " // 2nd case {name} " . format ( name = f . name ) )
typename = f . fieldtype . name
# If structs are varlen, need array of ptrs to them.
if basetype in varlen_structs :
typename + = ' * '
subcalls . append ( ' {} -> {} = {} ? tal_arr(ctx, {} , {} ) : NULL; '
. format ( self . name , f . name , f . lenvar , typename , f . lenvar ) )
name = ' {} -> {} ' . format ( self . name , f . name )
# Allocate these off the array itself, if they need alloc.
self . print_fromwire_array ( ' * ' + f . name , subcalls , basetype , f ,
name , f . lenvar , is_tlv = True )
else :
if f . is_assignable ( ) :
if f . is_len_var :
s = ' {} = fromwire_ {} (cursor, plen); ' . format ( f . name , basetype )
else :
s = ' {} -> {} = fromwire_ {} (cursor, plen); ' . format (
self . name , f . name , basetype )
else :
2019-03-28 03:00:07 +01:00
s = ' fromwire_ {} (cursor, plen, & {} -> {} ); ' . format (
2019-03-27 23:48:45 +01:00
basetype , self . name , f . name )
subcalls . append ( s )
return fromwire_tlv_impl_templ . format (
tlv_name = tlv_name ,
name = self . name ,
ctx = ctx_arg ,
args = ' ' . join ( args ) ,
fields = ' ' . join ( fields ) ,
subcalls = str ( subcalls )
)
2019-03-28 22:19:12 +01:00
2019-03-28 22:45:07 +01:00
class Subtype ( Message ) :
def __init__ ( self , name , comments ) :
super ( ) . __init__ ( name , None , comments , False )
def print_struct ( self ) :
return TlvMessage . _inner_print_struct ( self . name , self . fields )
2019-03-28 02:58:29 +01:00
def print_towire ( self ) :
""" prints towire function definition for a subtype """
2019-04-09 01:43:14 +02:00
template = subtype_towire_header_stub if options . header else subtype_towire_stub
2019-03-28 02:58:29 +01:00
field_decls = [ ]
for f in self . fields :
if f . optional :
raise TypeError ( " Optional fields on subtypes not currently supported. {} " . format ( f . name ) )
if f . is_len_var :
field_decls . append ( ' \t {0} {1} = tal_count( {2} -> {3} ); ' . format (
f . fieldtype . name , f . name , self . name , f . lenvar_for . name
) )
subcalls = CCode ( )
for f in self . fields :
basetype = f . fieldtype . base ( )
for c in f . comments :
subcalls . append ( ' /* {} */ ' . format ( c ) )
if f . is_padding ( ) :
subcalls . append ( ' towire_pad(p, {} ); ' . format ( f . num_elems ) )
elif f . is_array ( ) :
self . print_towire_array ( subcalls , basetype , f , f . num_elems ,
is_tlv = True )
elif f . is_variable_size ( ) :
self . print_towire_array ( subcalls , basetype , f , f . lenvar ,
is_tlv = True )
elif f . is_len_var :
subcalls . append ( ' towire_ {} (p, {} ); ' . format ( basetype , f . name ) )
else :
ref = ' & ' if f . fieldtype . needs_ptr ( ) else ' '
subcalls . append ( ' towire_ {} (p, {} {} -> {} ); ' . format ( basetype , ref , self . name , f . name ) )
2019-04-09 01:43:14 +02:00
return template . format (
static = ' ' if options . subtypes else ' static ' ,
2019-03-28 02:58:29 +01:00
name = self . name ,
field_decls = ' \n ' . join ( field_decls ) ,
subcalls = str ( subcalls ) )
def print_fromwire ( self ) :
""" prints fromwire function definition for a subtype.
these are significantly different in that they take in a struct
to populate , instead of fields .
"""
ctx_arg = ' const tal_t *ctx, ' if self . has_variable_fields else ' '
args = ' const u8 **cursor, size_t *plen, struct {name} * {name} ' . format ( name = self . name )
fields = [ ' \t {} {} ; \n ' . format ( f . fieldtype . name , f . name ) for f in self . fields if f . is_len_var ]
2019-04-09 01:43:14 +02:00
template = fromwire_subtype_header_templ if options . header else fromwire_subtype_impl_templ
2019-03-28 02:58:29 +01:00
subcalls = CCode ( )
for f in self . fields :
basetype = f . fieldtype . base ( )
if f . optional :
raise TypeError ( ' Optional fields on subtypes not currently supported ' )
for c in f . comments :
subcalls . append ( ' /* {} */ ' . format ( c ) )
if f . is_padding ( ) :
subcalls . append ( ' fromwire_pad(cursor, plen, {} ); '
. format ( f . num_elems ) )
elif f . is_array ( ) :
name = ' * {} -> {} ' . format ( self . name , f . name )
self . print_fromwire_array ( ' ctx ' , subcalls , basetype , f , name ,
f . num_elems , is_tlv = True )
elif f . is_variable_size ( ) :
subcalls . append ( " // 2nd case {name} " . format ( name = f . name ) )
typename = f . fieldtype . name
# If structs are varlen, need array of ptrs to them.
if basetype in varlen_structs :
typename + = ' * '
subcalls . append ( ' {} -> {} = {} ? tal_arr(ctx, {} , {} ) : NULL; '
. format ( self . name , f . name , f . lenvar , typename , f . lenvar ) )
name = ' {} -> {} ' . format ( self . name , f . name )
# Allocate these off the array itself, if they need alloc.
self . print_fromwire_array ( ' * ' + f . name , subcalls , basetype , f ,
name , f . lenvar , is_tlv = True )
else :
if f . is_assignable ( ) :
if f . is_len_var :
s = ' {} = fromwire_ {} (cursor, plen); ' . format ( f . name , basetype )
else :
s = ' {} -> {} = fromwire_ {} (cursor, plen); ' . format (
self . name , f . name , basetype )
else :
ref = ' & ' if f . fieldtype . needs_ptr ( ) else ' '
s = ' fromwire_ {} (cursor, plen, {} {} -> {} ); ' . format (
basetype , ref , self . name , f . name )
subcalls . append ( s )
2019-04-09 01:43:14 +02:00
return template . format (
static = ' ' if options . subtypes else ' static ' ,
2019-03-28 02:58:29 +01:00
name = self . name ,
ctx = ctx_arg ,
args = ' ' . join ( args ) ,
fields = ' ' . join ( fields ) ,
subcalls = str ( subcalls )
)
2019-03-28 22:45:07 +01:00
2019-03-28 22:26:25 +01:00
tlv_message_towire_stub = """ static void towire_ {tlv_name} _ {name} (u8 **p, struct tlv_msg_ {name} * {name} ) {{
2019-03-28 22:23:18 +01:00
{ field_decls }
{ subcalls }
} }
"""
2019-04-09 01:43:14 +02:00
subtype_towire_stub = """ {static} void towire_ {name} (u8 **p, const struct {name} * {name} ) {{
2019-03-28 02:58:29 +01:00
{ field_decls }
{ subcalls }
} }
"""
2019-04-09 01:43:14 +02:00
subtype_towire_header_stub = """ void towire_ {name} (u8 **p, const struct {name} * {name} ); """
2019-03-28 22:20:39 +01:00
tlv_struct_template = """
2019-03-28 22:26:25 +01:00
struct { tlv_name } { {
2019-03-28 22:20:39 +01:00
{ msg_type_structs }
} } ;
"""
2019-03-28 22:23:18 +01:00
tlv__type_impl_towire_fields = """ \t if ( {tlv_name} -> {name} ) {{
2019-03-28 22:25:19 +01:00
\t \ttlv_msg = tal_arr ( ctx , u8 , 0 ) ;
2019-03-26 02:33:29 +01:00
\t \ttowire_u8 ( p , { enum } ) ;
2019-03-28 22:26:25 +01:00
\t \ttowire_ { tlv_name } _ { name } ( & tlv_msg , { tlv_name } - > { name } ) ;
2019-03-28 22:25:19 +01:00
\t \tmsg_len = tal_count ( tlv_msg ) ;
2019-04-03 04:30:48 +02:00
\t \ttowire_var_int ( p , msg_len ) ;
2019-03-28 22:25:19 +01:00
\t \ttowire_u8_array ( p , tlv_msg , msg_len ) ;
2019-03-26 01:48:01 +01:00
\t \ttal_free ( tlv_msg ) ;
2019-03-28 22:23:18 +01:00
\t } }
"""
2019-03-28 22:26:25 +01:00
tlv__type_impl_towire_template = """ static void towire__ {tlv_name} (const tal_t *ctx, u8 **p, const struct {tlv_name} * {tlv_name} ) {{
2019-04-03 04:30:48 +02:00
\tu64 msg_len ;
2019-03-28 22:25:19 +01:00
\tu8 * tlv_msg ;
2019-03-28 22:23:18 +01:00
{ fields } } }
"""
2019-04-03 04:30:48 +02:00
tlv__type_impl_fromwire_template = """ static struct {tlv_name} *fromwire__ {tlv_name} (const tal_t *ctx, const u8 **p, size_t *plen, const u64 *len) {{
\tu8 msg_type ;
\tu64 msg_len ;
2019-03-25 03:35:25 +01:00
\tsize_t start_len = * plen ;
2019-03-28 22:25:19 +01:00
\tif ( * plen < * len )
2019-03-28 22:27:28 +01:00
\t \treturn NULL ;
\tstruct { tlv_name } * { tlv_name } = talz ( ctx , struct { tlv_name } ) ;
2019-03-28 22:23:18 +01:00
2019-03-28 22:25:19 +01:00
\twhile ( * plen ) { {
2019-03-26 02:33:29 +01:00
\t \tmsg_type = fromwire_u8 ( p , plen ) ;
2019-04-03 04:30:48 +02:00
\t \tmsg_len = fromwire_var_int ( p , plen ) ;
2019-03-28 22:25:19 +01:00
\t \tif ( * plen < msg_len ) { {
\t \t \tfromwire_fail ( p , plen ) ;
2019-03-28 22:23:18 +01:00
\t \t \tbreak ;
\t \t } }
\t \tswitch ( ( enum { tlv_name } _type ) msg_type ) { {
{ cases } \t \tdefault :
2019-03-25 00:23:34 +01:00
\t \t \tif ( msg_type % 2 == 0 ) { { / / it ' s ok to be odd
\t \t \t \tfromwire_fail ( p , plen ) ;
\t \t \t \ttal_free ( { tlv_name } ) ;
\t \t \t \treturn NULL ;
\t \t \t } }
\t \t \t * p + = msg_len ;
\t \t \t * plen - = msg_len ;
2019-03-28 22:23:18 +01:00
\t \t } }
\t } }
2019-03-25 03:35:25 +01:00
\tif ( ! * p | | start_len - * plen != * len ) { {
2019-03-28 22:27:28 +01:00
\t \ttal_free ( { tlv_name } ) ;
\t \treturn NULL ;
\t } }
\treturn { tlv_name } ;
2019-03-28 22:23:18 +01:00
} }
"""
case_tmpl = """ \t \t case {tlv_msg_enum} :
2019-03-28 22:27:28 +01:00
\t \t \tif ( { tlv_name } - > { tlv_msg_name } != NULL ) { {
\t \t \t \tfromwire_fail ( p , plen ) ;
\t \t \t \ttal_free ( { tlv_name } ) ;
\t \t \t \treturn NULL ;
\t \t \t } }
\t \t \t { tlv_name } - > { tlv_msg_name } = tal ( { tlv_name } , struct tlv_msg_ { tlv_msg_name } ) ;
2019-03-28 22:28:56 +01:00
\t \t \tif ( ! fromwire_ { tlv_name } _ { tlv_msg_name } ( { ctx_arg } p , plen , msg_len , { tlv_name } - > { tlv_msg_name } ) ) { {
2019-03-28 22:27:28 +01:00
\t \t \t \ttal_free ( { tlv_name } ) ;
\t \t \t \treturn NULL ;
\t \t \t } }
2019-03-28 22:23:18 +01:00
\t \t \tbreak ;
"""
2019-04-05 05:06:49 +02:00
print_tlv_template = """ static void printwire_ {tlv_name} (const char *fieldname, const u8 *cursor)
{ {
\tu8 msg_type ;
\tu64 msg_size ;
\tsize_t plen = tal_count ( cursor ) ;
\twhile ( cursor ) { {
\t \tmsg_type = fromwire_u8 ( & cursor , & plen ) ;
\t \tmsg_size = fromwire_var_int ( & cursor , & plen ) ;
\t \tif ( ! cursor )
\t \t \tbreak ;
\t \tswitch ( ( enum { tlv_name } _type ) msg_type ) { {
\t \t \t { printcases }
\t \t \tdefault :
\t \t \t \tprintf ( " WARNING:No message matching type %d \\ n " , msg_type ) ;
\t \t } }
\t } }
\tif ( plen != 0 )
\t \tprintf ( " EXTRA: %s \\ n " , tal_hexstr ( NULL , cursor , plen ) ) ;
} }
"""
2019-03-28 22:23:18 +01:00
def build_tlv_fromwires ( tlv_fields ) :
fromwires = [ ]
for field_name , messages in tlv_fields . items ( ) :
fromwires . append ( print_tlv_fromwire ( field_name , messages ) )
return fromwires
def build_tlv_towires ( tlv_fields ) :
towires = [ ]
for field_name , messages in tlv_fields . items ( ) :
towires . append ( print_tlv_towire ( field_name , messages ) )
return towires
def print_tlv_towire ( tlv_field_name , messages ) :
fields = " "
for m in messages :
fields + = tlv__type_impl_towire_fields . format (
tlv_name = tlv_field_name ,
enum = m . enum . name ,
name = m . name )
return tlv__type_impl_towire_template . format (
tlv_name = tlv_field_name ,
fields = fields )
def print_tlv_fromwire ( tlv_field_name , messages ) :
cases = " "
for m in messages :
2019-03-28 22:27:28 +01:00
ctx_arg = tlv_field_name + ' , ' if m . has_variable_fields else ' '
2019-03-28 22:23:18 +01:00
cases + = case_tmpl . format ( ctx_arg = ctx_arg ,
tlv_msg_enum = m . enum . name ,
tlv_name = tlv_field_name ,
tlv_msg_name = m . name )
return tlv__type_impl_fromwire_template . format (
tlv_name = tlv_field_name ,
cases = cases )
2019-03-28 22:20:39 +01:00
def build_tlv_type_struct ( name , messages ) :
inner_structs = CCode ( )
for m in messages :
2019-03-28 22:26:25 +01:00
inner_structs . append ( ' struct tlv_msg_ {} * {} ; ' . format ( m . name , m . name ) )
2019-03-28 22:20:39 +01:00
return tlv_struct_template . format (
tlv_name = name ,
msg_type_structs = str ( inner_structs ) )
def build_tlv_type_structs ( tlv_fields ) :
structs = ' '
for name , messages in tlv_fields . items ( ) :
structs + = build_tlv_type_struct ( name , messages )
return structs
2018-02-21 19:46:04 +01:00
2017-11-03 01:10:57 +01:00
def find_message ( messages , name ) :
for m in messages :
if m . name == name :
return m
return None
2018-02-21 19:46:04 +01:00
2019-04-05 05:06:49 +02:00
def print_tlv_printwire ( tlv_name , messages ) :
printcases = ' '
for m in messages :
printcases + = ' case {enum.name} : printf( " {enum.name} (size % " PRIu64 " ): \\ n " , msg_size); printwire_ {name} ( " {name} " , &cursor, &plen); break; ' . format (
enum = m . enum , name = m . name , tlv_name = tlv_name )
return print_tlv_template . format (
tlv_name = tlv_name ,
printcases = printcases )
def print_tlv_printwires ( tlv_fields ) :
decls = [ ]
switches = ' '
for name , messages in tlv_fields . items ( ) :
# Print each of the message parsers
decls + = [ m . print_printwire ( options . header , is_tlv = True ) for m in messages ]
# Print the TLV body parser
decls . append ( print_tlv_printwire ( name , messages ) )
# Print the 'master' print_tlv_messages cases
switches + = tlv_switch_template . format ( tlv_name = name )
decls . append ( print_master_tlv_template . format ( tlv_switches = switches ) )
return decls
2017-11-03 01:10:57 +01:00
def find_message_with_option ( messages , optional_messages , name , option ) :
2017-11-15 05:02:21 +01:00
fullname = name + " _ " + option . replace ( ' - ' , ' _ ' )
2017-11-03 01:10:57 +01:00
base = find_message ( messages , name )
if not base :
raise ValueError ( ' Unknown message {} ' . format ( name ) )
2017-11-15 05:02:21 +01:00
m = find_message ( optional_messages , fullname )
2017-11-03 01:10:57 +01:00
if not m :
# Add a new option.
m = copy . deepcopy ( base )
m . name = fullname
optional_messages . append ( m )
return m
2018-02-22 12:59:25 +01:00
2017-12-28 14:40:01 +01:00
parser = argparse . ArgumentParser ( description = ' Generate C from CSV ' )
2017-01-06 03:54:45 +01:00
parser . add_argument ( ' --header ' , action = ' store_true ' , help = " Create wire header " )
2019-04-09 01:43:14 +02:00
parser . add_argument ( ' --subtypes ' , action = ' store_true ' , help = " Include subtype parsing function delcarations in header definition. Only active if --header also declared. " )
2017-09-28 05:41:19 +02:00
parser . add_argument ( ' --bolt ' , action = ' store_true ' , help = " Generate wire-format for BOLT " )
2018-02-01 01:08:25 +01:00
parser . add_argument ( ' --printwire ' , action = ' store_true ' , help = " Create print routines " )
2017-01-06 03:54:45 +01:00
parser . add_argument ( ' headerfilename ' , help = ' The filename of the header ' )
parser . add_argument ( ' enumname ' , help = ' The name of the enum to produce ' )
parser . add_argument ( ' files ' , nargs = ' * ' , help = ' Files to read in (or stdin) ' )
options = parser . parse_args ( )
2016-11-29 20:51:50 +01:00
# Maps message names to messages
2017-01-04 04:39:21 +01:00
messages = [ ]
2017-11-03 01:10:57 +01:00
messages_with_option = [ ]
2019-03-28 22:45:07 +01:00
subtypes = [ ]
2017-01-04 04:39:21 +01:00
comments = [ ]
2017-01-06 03:54:45 +01:00
includes = [ ]
2019-04-03 04:28:44 +02:00
tlv_fields = { }
2017-03-16 05:05:25 +01:00
prevfield = None
2016-11-29 20:51:50 +01:00
# Read csv lines. Single comma is the message values, more is offset/len.
2017-01-06 03:54:45 +01:00
for line in fileinput . input ( options . files ) :
2017-01-06 03:54:45 +01:00
# #include gets inserted into header
if line . startswith ( ' #include ' ) :
includes . append ( line )
continue
2017-01-04 04:39:21 +01:00
by_comments = line . rstrip ( ) . split ( ' # ' )
# Emit a comment if they included one
if by_comments [ 1 : ] :
comments . append ( ' ' . join ( by_comments [ 1 : ] ) )
parts = by_comments [ 0 ] . split ( ' , ' )
if parts == [ ' ' ] :
continue
2016-11-29 20:51:50 +01:00
2019-04-03 04:29:48 +02:00
is_tlv_msg = len ( parts ) == 3
if len ( parts ) == 2 or is_tlv_msg :
# eg: commit_sig,132,(_tlv)
2019-03-28 22:45:07 +01:00
if parts [ 1 ] == ' $ ' : # this is a subtype
subtypes . append ( Subtype ( parts [ 0 ] , comments ) )
2019-03-27 23:39:58 +01:00
else :
2019-03-28 22:45:07 +01:00
if is_tlv_msg :
message = TlvMessage ( parts [ 0 ] ,
Enumtype ( " WIRE_ " + parts [ 0 ] . upper ( ) , parts [ 1 ] ) ,
comments )
else :
message = Message ( parts [ 0 ] ,
Enumtype ( " WIRE_ " + parts [ 0 ] . upper ( ) , parts [ 1 ] ) ,
comments )
2019-04-03 04:29:48 +02:00
2019-03-28 22:45:07 +01:00
messages . append ( message )
if is_tlv_msg :
tlv_fields [ parts [ 2 ] ] . append ( message )
2019-04-03 04:29:48 +02:00
2018-02-21 19:46:04 +01:00
comments = [ ]
2017-03-16 05:05:25 +01:00
prevfield = None
else :
2017-11-03 01:10:57 +01:00
if len ( parts ) == 4 :
# eg commit_sig,0,channel-id,8 OR
# commit_sig,0,channel-id,u64
2019-03-28 22:45:07 +01:00
m = find_message ( messages + subtypes , parts [ 0 ] )
2017-11-03 01:10:57 +01:00
elif len ( parts ) == 5 :
# eg.
# channel_reestablish,48,your_last_per_commitment_secret,32,option209
m = find_message_with_option ( messages , messages_with_option , parts [ 0 ] , parts [ 4 ] )
else :
raise ValueError ( ' Line {} malformed ' . format ( line . rstrip ( ) ) )
2019-03-28 03:00:25 +01:00
if m is None :
raise ValueError ( ' Unknown message or subtype {} ' . format ( parts [ 0 ] ) )
2019-03-09 02:50:52 +01:00
f = Field ( m . name , parts [ 2 ] , parts [ 3 ] , comments , prevfield , includes )
2017-11-03 01:10:57 +01:00
m . addField ( f )
# If it used prevfield as lenvar, keep that for next
# time (multiple fields can use the same lenvar).
if not f . lenvar :
prevfield = parts [ 2 ]
2018-02-21 19:46:04 +01:00
comments = [ ]
2016-11-29 20:51:50 +01:00
2019-04-03 04:28:44 +02:00
2019-03-28 22:17:07 +01:00
def construct_hdr_enums ( msgs ) :
2019-04-03 04:28:44 +02:00
enums = " "
for m in msgs :
for c in m . comments :
enums + = ' \t /* {} */ \n ' . format ( c )
enums + = ' \t {} = {} , \n ' . format ( m . enum . name , m . enum . value )
return enums
2019-03-28 22:17:07 +01:00
def construct_impl_enums ( msgs ) :
return ' \n \t ' . join ( [ ' case {enum.name} : return " {enum.name} " ; ' . format ( enum = m . enum ) for m in msgs ] )
2019-04-03 04:28:44 +02:00
def enum_header ( enums , enumname ) :
2019-03-28 22:17:07 +01:00
return format_enums ( enum_header_template , enums , enumname )
def enum_impl ( enums , enumname ) :
return format_enums ( enum_impl_template , enums , enumname )
def format_enums ( template , enums , enumname ) :
return template . format (
2019-04-03 04:28:44 +02:00
enums = enums ,
enumname = enumname )
2019-03-28 22:18:16 +01:00
def build_hdr_enums ( toplevel_enumname , toplevel_messages , tlv_fields ) :
2019-04-03 04:28:44 +02:00
enum_set = " "
2019-04-09 01:43:14 +02:00
if len ( toplevel_messages ) :
enum_set + = enum_header ( construct_hdr_enums ( toplevel_messages ) , toplevel_enumname )
2019-03-28 22:18:16 +01:00
for field_name , tlv_messages in tlv_fields . items ( ) :
2019-04-03 04:28:44 +02:00
enum_set + = " \n "
2019-03-28 22:18:16 +01:00
enum_set + = enum_header ( construct_hdr_enums ( tlv_messages ) , field_name + ' _type ' )
2019-03-28 22:17:07 +01:00
return enum_set
2019-03-28 22:18:16 +01:00
def build_impl_enums ( toplevel_enumname , toplevel_messages , tlv_fields ) :
2019-03-28 22:17:07 +01:00
enum_set = " "
2019-04-09 01:43:14 +02:00
if len ( toplevel_messages ) :
enum_set + = enum_impl ( construct_impl_enums ( toplevel_messages ) , toplevel_enumname )
2019-03-28 22:18:16 +01:00
for field_name , tlv_messages in tlv_fields . items ( ) :
2019-03-28 22:17:07 +01:00
enum_set + = " \n "
2019-03-28 22:18:16 +01:00
enum_set + = enum_impl ( construct_impl_enums ( tlv_messages ) , field_name + ' _type ' )
2019-04-03 04:28:44 +02:00
return enum_set
2019-03-28 22:19:12 +01:00
def build_tlv_structs ( tlv_fields ) :
structs = " "
for field_name , tlv_messages in tlv_fields . items ( ) :
for m in tlv_messages :
structs + = m . print_struct ( )
return structs
2019-03-28 22:45:07 +01:00
def build_subtype_structs ( subtypes ) :
structs = " "
for subtype in subtypes :
structs + = subtype . print_struct ( )
return structs
2019-04-03 04:28:44 +02:00
enum_header_template = """ enum {enumname} {{
{ enums }
} } ;
const char * { enumname } _name ( int e ) ;
"""
2019-03-28 22:17:07 +01:00
enum_impl_template = """
const char * { enumname } _name ( int e )
{ {
\tstatic char invalidbuf [ sizeof ( " INVALID " ) + STR_MAX_CHARS ( e ) ] ;
\tswitch ( ( enum { enumname } ) e ) { {
\t { enums }
\t } }
\tsnprintf ( invalidbuf , sizeof ( invalidbuf ) , " INVALID %i " , e ) ;
\treturn invalidbuf ;
} }
"""
2018-01-10 23:44:37 +01:00
header_template = """ /* This file was generated by generate-wire.py */
/ * Do not modify this file ! Modify the _csv file it was generated from . * /
#ifndef LIGHTNING_{idem}
2017-02-28 23:07:38 +01:00
#define LIGHTNING_{idem}
#include <ccan/tal/tal.h>
#include <wire/wire.h>
{ includes }
2019-03-28 22:45:07 +01:00
{ formatted_hdr_enums } { gen_structs }
2017-02-28 23:07:38 +01:00
{ func_decls }
#endif /* LIGHTNING_{idem} */
"""
2018-01-10 23:44:37 +01:00
impl_template = """ /* This file was generated by generate-wire.py */
/ * Do not modify this file ! Modify the _csv file it was generated from . * /
#include <{headerfilename}>
2017-02-28 23:07:38 +01:00
#include <ccan/mem/mem.h>
#include <ccan/tal/str/str.h>
#include <stdio.h>
2019-03-28 22:17:07 +01:00
{ formatted_impl_enums }
2017-02-28 23:07:38 +01:00
{ func_decls }
"""
2019-04-05 05:06:49 +02:00
print_tlv_message_printwire_empty = """ void print {enumname} _tlv_message(const char *tlv_name, const u8 *msg)
{ {
\tprintf ( " ~~ No TLV definition found for %s ~~ \\ n " , tlv_name ) ;
} }
"""
2018-02-01 01:08:25 +01:00
print_header_template = """ /* This file was generated by generate-wire.py */
/ * Do not modify this file ! Modify the _csv file it was generated from . * /
#ifndef LIGHTNING_{idem}
#define LIGHTNING_{idem}
#include <ccan/tal/tal.h>
#include <devtools/print_wire.h>
{ includes }
2018-07-26 23:20:37 +02:00
void print { enumname } _message ( const u8 * msg ) ;
2018-02-01 01:08:25 +01:00
2019-04-05 05:06:49 +02:00
void print { enumname } _tlv_message ( const char * tlv_name , const u8 * msg ) ;
2018-02-01 01:08:25 +01:00
{ func_decls }
#endif /* LIGHTNING_{idem} */
"""
print_template = """ /* This file was generated by generate-wire.py */
/ * Do not modify this file ! Modify the _csv file it was generated from . * /
#include "{headerfilename}"
#include <ccan/mem/mem.h>
#include <ccan/tal/str/str.h>
#include <common/utils.h>
2019-04-05 05:06:49 +02:00
#include <inttypes.h>
2018-02-01 01:08:25 +01:00
#include <stdio.h>
2018-07-26 23:20:37 +02:00
void print { enumname } _message ( const u8 * msg )
2018-02-01 01:08:25 +01:00
{ {
2018-06-19 10:38:51 +02:00
\tswitch ( ( enum { enumname } ) fromwire_peektype ( msg ) ) { {
\t { printcases }
\t } }
2018-02-01 01:08:25 +01:00
2018-06-19 10:38:51 +02:00
\tprintf ( " UNKNOWN: %s \\ n " , tal_hex ( msg , msg ) ) ;
2018-02-01 01:08:25 +01:00
} }
{ func_decls }
"""
2019-04-05 05:06:49 +02:00
print_master_tlv_template = """
void print_tlv_message ( const char * tlv_name , const u8 * msg )
{ {
\t { tlv_switches }
\tprintf ( " ERR: Unknown TLV message type: %s \n " , tlv_name ) ;
} }
"""
tlv_switch_template = """
\tif ( strcmp ( tlv_name , " {tlv_name} " ) == 0 ) { {
\t \tprintwire_ { tlv_name } ( " {tlv_name} " , msg ) ;
\t \treturn ;
\t } }
"""
2017-02-28 23:07:38 +01:00
idem = re . sub ( r ' [^A-Z]+ ' , ' _ ' , options . headerfilename . upper ( ) )
2018-02-01 01:08:25 +01:00
if options . printwire :
if options . header :
template = print_header_template
else :
template = print_template
elif options . header :
template = header_template
else :
template = impl_template
2016-11-29 20:51:50 +01:00
2019-03-28 22:18:16 +01:00
# Print out all the things
toplevel_messages = [ m for m in messages if not m . is_tlv ]
built_hdr_enums = build_hdr_enums ( options . enumname , toplevel_messages , tlv_fields )
built_impl_enums = build_impl_enums ( options . enumname , toplevel_messages , tlv_fields )
2019-03-28 22:19:12 +01:00
tlv_structs = build_tlv_structs ( tlv_fields )
2019-03-28 22:20:39 +01:00
tlv_structs + = build_tlv_type_structs ( tlv_fields )
2019-03-28 22:45:07 +01:00
subtype_structs = build_subtype_structs ( subtypes )
2017-02-28 23:07:38 +01:00
includes = ' \n ' . join ( includes )
2019-03-28 22:18:16 +01:00
printcases = [ ' case {enum.name} : printf( " {enum.name} : \\ n " ); printwire_ {name} ( " {name} " , msg); return; ' . format ( enum = m . enum , name = m . name ) for m in toplevel_messages ]
2017-02-28 23:07:38 +01:00
2018-02-01 01:08:25 +01:00
if options . printwire :
2019-04-05 05:06:49 +02:00
decls = [ m . print_printwire ( options . header ) for m in toplevel_messages + messages_with_option ]
if not options . header :
if len ( tlv_fields ) :
decls + = print_tlv_printwires ( tlv_fields )
else :
decls + = [ print_tlv_message_printwire_empty . format ( enumname = options . enumname ) ]
2018-02-01 01:08:25 +01:00
else :
2019-03-28 22:23:18 +01:00
towire_decls = [ ]
fromwire_decls = [ ]
for tlv_field , tlv_messages in tlv_fields . items ( ) :
for m in tlv_messages :
towire_decls . append ( m . print_towire ( options . header , tlv_field ) )
fromwire_decls . append ( m . print_fromwire ( options . header , tlv_field ) )
if not options . header :
2019-03-28 22:25:19 +01:00
towire_decls + = build_tlv_towires ( tlv_fields )
fromwire_decls + = build_tlv_fromwires ( tlv_fields )
2019-04-09 01:43:14 +02:00
if not options . header or options . header and options . subtypes :
2019-03-28 02:58:29 +01:00
for subtype in subtypes :
towire_decls . append ( subtype . print_towire ( ) )
fromwire_decls . append ( subtype . print_fromwire ( ) )
2019-03-28 22:23:18 +01:00
2019-03-27 23:58:02 +01:00
towire_decls + = [ m . print_towire ( options . header ) for m in toplevel_messages + messages_with_option ]
fromwire_decls + = [ m . print_fromwire ( options . header ) for m in toplevel_messages + messages_with_option ]
2018-02-01 01:08:25 +01:00
decls = fromwire_decls + towire_decls
2017-02-28 23:07:38 +01:00
print ( template . format (
headerfilename = options . headerfilename ,
2018-02-01 01:08:25 +01:00
printcases = ' \n \t ' . join ( printcases ) ,
2017-02-28 23:07:38 +01:00
idem = idem ,
includes = includes ,
enumname = options . enumname ,
2019-03-28 22:17:07 +01:00
formatted_hdr_enums = built_hdr_enums ,
formatted_impl_enums = built_impl_enums ,
2019-03-28 22:45:07 +01:00
gen_structs = tlv_structs + subtype_structs ,
2018-02-22 12:59:25 +01:00
func_decls = ' \n ' . join ( decls ) ) )