@@ -959,157 +959,85 @@ def to_binary(self, *varargs, **keys):
959959
960960 """
961961
962- code = ''
963- total_length = str (self .static_size )
964- joins = []
965- args = []
966- defargs = []
967- kwarg = 0
962+ # Emulate Python function argument handling with our field names
963+ names = [f .name for f in self .fields \
964+ if isinstance (f , ValueField ) and f .name ]
965+ field_args = dict (zip (names , varargs ))
966+ if set (field_args ).intersection (keys ):
967+ dupes = ", " .join (set (field_args ).intersection (keys ))
968+ raise TypeError ("%s arguments were passed both positionally and by keyword" % dupes )
969+ field_args .update (keys )
970+ for f in self .fields :
971+ if f .name and (f .name not in field_args ):
972+ if f .default is None :
973+ raise TypeError ("Missing required argument %s" % f .name )
974+ field_args [f .name ] = f .default
975+ # /argument handling
968976
969977 # First pack all varfields so their lengths and formats are
970978 # available when we pack their static LengthFields and
971979 # FormatFields
972980
973- i = 0
981+ total_length = self .static_size
982+ var_vals = {}
983+ lengths = {}
984+ formats = {}
985+
974986 for f in self .var_fields :
975987 if f .keyword_args :
976- kwarg = 1
977- kw = ', _keyword_args'
988+ v , l , fm = f .pack_value (field_args [f .name ], keys )
978989 else :
979- kw = ''
980-
981- # Call pack_value method for each field, storing
982- # the return values for later use
983- code = code + (' _%(name)s, _%(name)s_length, _%(name)s_format'
984- ' = self.var_fields[%(fno)d].pack_value(%(name)s%(kw)s)\n '
985- % { 'name' : f .name ,
986- 'fno' : i ,
987- 'kw' : kw })
988-
989- total_length = total_length + ' + len(_%s)' % f .name
990- joins .append ('_%s' % f .name )
990+ v , l , fm = f .pack_value (field_args [f .name ])
991+ var_vals [f .name ] = v
992+ lengths [f .name ] = l
993+ formats [f .name ] = fm
991994
992- i = i + 1
995+ total_length += len ( v )
993996
994997
995- # Construct argument list for struct.pack call, packing all
996- # static fields. First argument is the structcode, the
997- # remaining are values.
998+ # Construct item list for struct.pack call, packing all static fields.
999+ pack_items = []
9981000
999- pack_args = ['"%s"' % self .static_codes ]
1000-
1001- i = 0
10021001 for f in self .static_fields :
10031002 if isinstance (f , LengthField ):
10041003
10051004 # If this is a total length field, insert
10061005 # the calculated field value here
10071006 if isinstance (f , TotalLengthField ):
1008- if self .var_fields :
1009- pack_args .append ('self.static_fields[%d].calc_length(%s)'
1010- % (i , total_length ))
1011- else :
1012- pack_args .append (str (f .calc_length (self .static_size )))
1007+ pack_items .append (f .calc_length (total_length ))
10131008 else :
1014- pack_args .append ('self.static_fields[%d].calc_length(_%s_length)'
1015- % (i , f .name ))
1009+ pack_items .append (f .calc_length (lengths [f .name ]))
10161010
10171011 # Format field, just insert the value we got previously
10181012 elif isinstance (f , FormatField ):
1019- pack_args .append ('_%s_format' % f .name )
1013+ pack_items .append (formats [ f .name ] )
10201014
10211015 # A constant field, insert its value directly
10221016 elif isinstance (f , ConstantField ):
1023- pack_args .append (str ( f .value ) )
1017+ pack_items .append (f .value )
10241018
10251019 # Value fields
10261020 else :
10271021 if f .structvalues == 1 :
1028-
10291022 # If there's a value check/convert function, call it
10301023 if f .check_value is not None :
1031- pack_args .append ('self.static_fields[%d].check_value(%s)'
1032- % (i , f .name ))
1033-
1024+ pack_items .append (f .check_value (field_args [f .name ]))
10341025 # Else just use the argument as provided
10351026 else :
1036- pack_args .append (f .name )
1027+ pack_items .append (field_args [ f .name ] )
10371028
10381029 # Multivalue field. Handled like single valuefield,
10391030 # but the value are tuple unpacked into seperate arguments
1040- # which are appended to pack_args
1031+ # which are appended to pack_items
10411032 else :
1042- a = []
1043- for j in range (f .structvalues ):
1044- a .append ('_%s_%d' % (f .name , j ))
1045-
10461033 if f .check_value is not None :
1047- code = code + (' %s = self.static_fields[%d].check_value(%s)\n '
1048- % (string .join (a , ', ' ), i , f .name ))
1034+ pack_items .extend (f .check_value (field_args [f .name ]))
10491035 else :
1050- code = code + ' %s = %s\n ' % (string .join (a , ', ' ), f .name )
1051-
1052- pack_args = pack_args + a
1053-
1054- # Add field to argument list
1055- if f .name :
1056- if f .default is None :
1057- args .append (f .name )
1058- else :
1059- defargs .append ('%s = %s' % (f .name , repr (f .default )))
1060-
1061- i = i + 1
1062-
1063-
1064- # Construct call to struct.pack
1065- pack = 'struct.pack(%s)' % ', ' .join (pack_args )
1066-
1067- # If there are any varfields, we append the packed strings to build
1068- # the resulting binary value
1069- if self .var_fields :
1070- code = code + ' return %s + %s\n ' % (pack , ' + ' .join (joins ))
1071-
1072- # If there's only static fields, return the packed value
1073- else :
1074- code = code + ' return %s\n ' % pack
1075-
1076- # Add all varsize fields to argument list. We do it here
1077- # to ensure that they appear after the static fields.
1078- for f in self .var_fields :
1079- if f .name :
1080- if f .default is None :
1081- args .append (f .name )
1082- else :
1083- defargs .append ('%s = %s' % (f .name , repr (f .default )))
1084-
1085- args = args + defargs
1086- if kwarg :
1087- args .append ('**_keyword_args' )
1088-
1089- # Add function header
1090- code = 'def to_binary(self, %s):\n ' % ', ' .join (args ) + code
1091-
1092- # self._pack_code = code
1093-
1094- # print
1095- # print code
1096- # print
1097-
1098- # Finally, compile function by evaluating it. This will store
1099- # the function in the local variable to_binary, thanks to the
1100- # def: line. Convert it into a instance metod bound to self,
1101- # and store it in self.
1102-
1103- # Unfortunately, this creates a circular reference. However,
1104- # Structs are not really created dynamically so the potential
1105- # memory leak isn't that serious. Besides, Python 2.0 has
1106- # real garbage collect.
1107- ns = {'struct' : struct }
1108- exec (code , ns )
1109- self .to_binary = _method (ns ['to_binary' ], self )
1036+ pack_items .extend (field_args [f .name ])
11101037
1111- # Finally call it manually
1112- return self .to_binary (* varargs , ** keys )
1038+ static_part = struct .pack (self .static_codes , * pack_items )
1039+ var_parts = [var_vals [f .name ] for f in self .var_fields ]
1040+ return static_part + b'' .join (var_parts )
11131041
11141042
11151043 def pack_value (self , value ):
@@ -1120,9 +1048,9 @@ def pack_value(self, value):
11201048
11211049 """
11221050
1123- if type (value ) is types . TupleType :
1051+ if type (value ) is tuple :
11241052 return self .to_binary (* value )
1125- elif type (value ) is types . DictionaryType :
1053+ elif type (value ) is dict :
11261054 return self .to_binary (** value )
11271055 elif isinstance (value , DictWrapper ):
11281056 return self .to_binary (** value ._data )
@@ -1136,12 +1064,8 @@ def parse_value(self, val, display, rawdict = 0):
11361064 Struct objects with no var_fields into Python values.
11371065
11381066 """
1139-
1140- code = ('def parse_value(self, val, display, rawdict = 0):\n '
1141- ' ret = {}\n ' )
1142-
1067+ ret = {}
11431068 vno = 0
1144- fno = 0
11451069 for f in self .static_fields :
11461070 # Fields without names should be ignored, and there should
11471071 # not be any length or format fields if this function
@@ -1160,42 +1084,22 @@ def parse_value(self, val, display, rawdict = 0):
11601084
11611085 # Value fields
11621086 else :
1163-
1164- # Get the index or range in val representing this field.
1165-
1166- if f .structvalues == 1 :
1167- vrange = str (vno )
1168- else :
1169- vrange = '%d:%d' % (vno , vno + f .structvalues )
1170-
11711087 # If this field has a parse_value method, call it, otherwise
11721088 # use the unpacked value as is.
1173-
1174- if f .parse_value is None :
1175- code = code + ' ret["%s"] = val[%s]\n ' % (f .name , vrange )
1089+ if f .structvalues == 1 :
1090+ field_val = val [vno ]
11761091 else :
1177- code = code + (' ret["%s"] = self.static_fields[%d].'
1178- 'parse_value(val[%s], display)\n '
1179- % (f .name , fno , vrange ))
1180-
1181- fno = fno + 1
1182- vno = vno + f .structvalues
1092+ field_val = val [vno :vno + f .structvalues ]
11831093
1184- code = code + ' if not rawdict: return DictWrapper(ret)\n '
1185- code = code + ' return ret\n '
1094+ if f .parse_value is not None :
1095+ field_val = f .parse_value (field_val , display , rawdict = rawdict )
1096+ ret [f .name ] = field_val
11861097
1187- # print
1188- # print code
1189- # print
1190-
1191- # Finally, compile function as for to_binary.
1192- ns = {'DictWrapper' : DictWrapper }
1193- exec (code , ns )
1194- self .parse_value = types .MethodType (ns ['parse_value' ], self , self .__class__ )
1195-
1196- # Call it manually
1197- return self .parse_value (val , display , rawdict )
1098+ vno = vno + f .structvalues
11981099
1100+ if not rawdict :
1101+ return DictWrapper (ret )
1102+ return ret
11991103
12001104 def parse_binary (self , data , display , rawdict = 0 ):
12011105
@@ -1216,17 +1120,12 @@ def parse_binary(self, data, display, rawdict = 0):
12161120 REMDATA are the remaining binary data, unused by the Struct object.
12171121
12181122 """
1219-
1220- code = ('def parse_binary(self, data, display, rawdict = 0):\n '
1221- ' ret = {}\n '
1222- ' val = struct.unpack("%s", data[:%d])\n '
1223- % (self .static_codes , self .static_size ))
1224-
1123+ ret = {}
1124+ val = struct .unpack (self .static_codes , data [:self .static_size ])
12251125 lengths = {}
12261126 formats = {}
12271127
12281128 vno = 0
1229- fno = 0
12301129 for f in self .static_fields :
12311130
12321131 # Fields without name should be ignored. This is typically
@@ -1242,63 +1141,42 @@ def parse_binary(self, data, display, rawdict = 0):
12421141 f_names = [f .name ]
12431142 if f .other_fields :
12441143 f_names .extend (f .other_fields )
1144+ field_val = val [vno ]
1145+ if f .parse_value is not None :
1146+ field_val = f .parse_value (field_val , display )
12451147 for f_name in f_names :
1246- if f .parse_value is None :
1247- lengths [f_name ] = 'val[%d]' % vno
1248- else :
1249- lengths [f_name ] = ('self.static_fields[%d].'
1250- 'parse_value(val[%d], display)'
1251- % (fno , vno ))
1148+ lengths [f_name ] = field_val
12521149
12531150 elif isinstance (f , FormatField ):
1254- formats [f .name ] = ' val[%d]' % vno
1151+ formats [f .name ] = val [vno ]
12551152
12561153 # Treat value fields the same was as in parse_value.
12571154 else :
12581155 if f .structvalues == 1 :
1259- vrange = str ( vno )
1156+ field_val = val [ vno ]
12601157 else :
1261- vrange = '%d:%d' % ( vno , vno + f .structvalues )
1158+ field_val = val [ vno : vno + f .structvalues ]
12621159
1263- if f .parse_value is None :
1264- code = code + ' ret["%s"] = val[%s]\n ' % (f .name , vrange )
1265- else :
1266- code = code + (' ret["%s"] = self.static_fields[%d].'
1267- 'parse_value(val[%s], display)\n '
1268- % (f .name , fno , vrange ))
1160+ if f .parse_value is not None :
1161+ field_val = f .parse_value (field_val , display )
1162+ ret [f .name ] = field_val
12691163
1270- fno = fno + 1
12711164 vno = vno + f .structvalues
12721165
1273- code = code + ' data = data[%d:] \n ' % self .static_size
1166+ data = data [self .static_size :]
12741167
12751168 # Call parse_binary_value for each var_field, passing the
12761169 # length and format values from the unpacked val.
12771170
1278- fno = 0
12791171 for f in self .var_fields :
1280- code = code + (' ret["%s"], data = '
1281- 'self.var_fields[%d].parse_binary_value'
1282- '(data, display, %s, %s)\n '
1283- % (f .name , fno ,
1284- lengths .get (f .name , 'None' ),
1285- formats .get (f .name , 'None' )))
1286- fno = fno + 1
1287-
1288- code = code + ' if not rawdict: ret = DictWrapper(ret)\n '
1289- code = code + ' return ret, data\n '
1290-
1291- # print
1292- # print code
1293- # print
1294-
1295- # Finally, compile function as for to_binary.
1296- ns = {'struct' : struct , 'DictWrapper' : DictWrapper }
1297- exec (code , ns )
1298- self .parse_binary = _method (ns ['parse_binary' ], self )
1299-
1300- # Call it manually
1301- return self .parse_binary (data , display , rawdict )
1172+ ret [f .name ], data = f .parse_binary_value (data , display ,
1173+ lengths .get (f .name , None ),
1174+ formats .get (f .name , None ),
1175+ )
1176+
1177+ if not rawdict :
1178+ ret = DictWrapper (ret )
1179+ return ret , data
13021180
13031181
13041182class TextElements8 (ValueField ):
0 commit comments