@@ -1596,32 +1596,73 @@ def m2i(self, pkt, x):
15961596
15971597
15981598class BitField (Field ):
1599- __slots__ = ["rev" , "size" ]
1599+ """
1600+ Field to handle bits.
1601+
1602+ :param name: name of the field
1603+ :param default: default value
1604+ :param size: size (in bits). If negative, Low endian
1605+ :param tot_size: size of the total group of bits (in bytes) the bitfield
1606+ is in. If negative, Low endian.
1607+ :param end_tot_size: same but for the BitField ending a group.
1608+
1609+ Example - normal usage::
1610+
1611+ 0 1 2 3
1612+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1613+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1614+ | A | B | C |
1615+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1616+
1617+ Fig. TestPacket
1618+
1619+ class TestPacket(Packet):
1620+ fields_desc = [
1621+ BitField("a", 0, 14),
1622+ BitField("b", 0, 16),
1623+ BitField("c", 0, 2),
1624+ ]
1625+
1626+ Example - Low endian stored as 16 bits on the network::
1627+
1628+ x x x x x x x x x x x x x x x x
1629+ a [b] [ c ] [ a ]
1630+
1631+ Will first get reversed during dissecion:
1632+
1633+ x x x x x x x x x x x x x x x x
1634+ [ a ] [b] [ c ]
1635+
1636+ class TestPacket(Packet):
1637+ fields_desc = [
1638+ BitField("a", 0, 9, tot_size=-16),
1639+ BitField("b", 0, 2),
1640+ BitField("c", 0, 5, end_tot_size=-16)
1641+ ]
1642+
1643+ """
1644+ __slots__ = ["rev" , "size" , "tot_size" , "end_tot_size" ]
16001645
1601- def __init__ (self , name , default , size ):
1646+ def __init__ (self , name , default , size ,
1647+ tot_size = 0 , end_tot_size = 0 ):
16021648 Field .__init__ (self , name , default )
1603- self .rev = size < 0
1649+ self .rev = size < 0 or tot_size < 0 or end_tot_size < 0
16041650 self .size = abs (size )
1651+ if not tot_size :
1652+ tot_size = self .size // 8
1653+ self .tot_size = abs (tot_size )
1654+ if not end_tot_size :
1655+ end_tot_size = self .size // 8
1656+ self .end_tot_size = abs (end_tot_size )
16051657 self .sz = self .size / 8.
16061658
1607- def reverse (self , val ):
1608- if self .size == 16 :
1609- # Replaces socket.ntohs (but work on both little/big endian)
1610- val = struct .unpack ('>H' , struct .pack ('<H' , int (val )))[0 ]
1611- elif self .size == 32 :
1612- # Same here but for socket.ntohl
1613- val = struct .unpack ('>I' , struct .pack ('<I' , int (val )))[0 ]
1614- return val
1615-
16161659 def addfield (self , pkt , s , val ):
16171660 val = self .i2m (pkt , val )
16181661 if isinstance (s , tuple ):
16191662 s , bitsdone , v = s
16201663 else :
16211664 bitsdone = 0
16221665 v = 0
1623- if self .rev :
1624- val = self .reverse (val )
16251666 v <<= self .size
16261667 v |= val & ((1 << self .size ) - 1 )
16271668 bitsdone += self .size
@@ -1632,13 +1673,20 @@ def addfield(self, pkt, s, val):
16321673 if bitsdone :
16331674 return s , bitsdone , v
16341675 else :
1676+ # Apply LE if necessary
1677+ if self .rev and self .end_tot_size > 1 :
1678+ s = s [:- self .end_tot_size ] + s [- self .end_tot_size :][::- 1 ]
16351679 return s
16361680
16371681 def getfield (self , pkt , s ):
16381682 if isinstance (s , tuple ):
16391683 s , bn = s
16401684 else :
16411685 bn = 0
1686+ # Apply LE if necessary
1687+ if self .rev and self .tot_size > 1 :
1688+ s = s [:self .tot_size ][::- 1 ] + s [self .tot_size :]
1689+
16421690 # we don't want to process all the string
16431691 nb_bytes = (self .size + bn - 1 ) // 8 + 1
16441692 w = s [:nb_bytes ]
@@ -1656,9 +1704,6 @@ def getfield(self, pkt, s):
16561704 # remove low order bits
16571705 b = b >> (nb_bytes * 8 - self .size - bn )
16581706
1659- if self .rev :
1660- b = self .reverse (b )
1661-
16621707 bn += self .size
16631708 s = s [bn // 8 :]
16641709 bn = bn % 8
@@ -2120,7 +2165,7 @@ class FlagsField(BitField):
21202165
21212166 :param name: field's name
21222167 :param default: default value for the field
2123- :param size: number of bits in the field
2168+ :param size: number of bits in the field (in bits)
21242169 :param names: (list or dict) label for each flag, Least Significant Bit tag's name is written first # noqa: E501
21252170 """
21262171 ismutable = True
@@ -2402,7 +2447,50 @@ def i2repr(self, pkt, x):
24022447 return "%s sec" % x
24032448
24042449
2405- class ScalingField (Field ):
2450+ class _ScalingField (object ):
2451+ def __init__ (self , name , default , scaling = 1 , unit = "" ,
2452+ offset = 0 , ndigits = 3 , fmt = "B" ):
2453+ self .scaling = scaling
2454+ self .unit = unit
2455+ self .offset = offset
2456+ self .ndigits = ndigits
2457+ Field .__init__ (self , name , default , fmt )
2458+
2459+ def i2m (self , pkt , x ):
2460+ if x is None :
2461+ x = 0
2462+ x = (x - self .offset ) / self .scaling
2463+ if isinstance (x , float ) and self .fmt [- 1 ] != "f" :
2464+ x = int (round (x ))
2465+ return x
2466+
2467+ def m2i (self , pkt , x ):
2468+ x = x * self .scaling + self .offset
2469+ if isinstance (x , float ) and self .fmt [- 1 ] != "f" :
2470+ x = round (x , self .ndigits )
2471+ return x
2472+
2473+ def any2i (self , pkt , x ):
2474+ if isinstance (x , (str , bytes )):
2475+ x = struct .unpack (self .fmt , bytes_encode (x ))[0 ]
2476+ x = self .m2i (pkt , x )
2477+ return x
2478+
2479+ def i2repr (self , pkt , x ):
2480+ return "%s %s" % (self .i2h (pkt , x ), self .unit )
2481+
2482+ def randval (self ):
2483+ value = super (_ScalingField , self ).randval ()
2484+ if value is not None :
2485+ min_val = round (value .min * self .scaling + self .offset ,
2486+ self .ndigits )
2487+ max_val = round (value .max * self .scaling + self .offset ,
2488+ self .ndigits )
2489+
2490+ return RandFloat (min (min_val , max_val ), max (min_val , max_val ))
2491+
2492+
2493+ class ScalingField (_ScalingField , Field ):
24062494 """ Handle physical values which are scaled and/or offset for communication
24072495
24082496 Example:
@@ -2442,48 +2530,15 @@ class ScalingField(Field):
24422530 :param ndigits: number of fractional digits for the internal conversion
24432531 :param fmt: struct.pack format used to parse and serialize the internal value from and to machine representation # noqa: E501
24442532 """
2445- __slots__ = ["scaling" , "unit" , "offset" , "ndigits" ]
2446-
2447- def __init__ (self , name , default , scaling = 1 , unit = "" ,
2448- offset = 0 , ndigits = 3 , fmt = "B" ):
2449- self .scaling = scaling
2450- self .unit = unit
2451- self .offset = offset
2452- self .ndigits = ndigits
2453- Field .__init__ (self , name , default , fmt )
2454-
2455- def i2m (self , pkt , x ):
2456- if x is None :
2457- x = 0
2458- x = (x - self .offset ) / self .scaling
2459- if isinstance (x , float ) and self .fmt [- 1 ] != "f" :
2460- x = int (round (x ))
2461- return x
24622533
2463- def m2i (self , pkt , x ):
2464- x = x * self .scaling + self .offset
2465- if isinstance (x , float ) and self .fmt [- 1 ] != "f" :
2466- x = round (x , self .ndigits )
2467- return x
24682534
2469- def any2i (self , pkt , x ):
2470- if isinstance (x , (str , bytes )):
2471- x = struct .unpack (self .fmt , bytes_encode (x ))[0 ]
2472- x = self .m2i (pkt , x )
2473- return x
2474-
2475- def i2repr (self , pkt , x ):
2476- return "%s %s" % (self .i2h (pkt , x ), self .unit )
2477-
2478- def randval (self ):
2479- value = super (ScalingField , self ).randval ()
2480- if value is not None :
2481- min_val = round (value .min * self .scaling + self .offset ,
2482- self .ndigits )
2483- max_val = round (value .max * self .scaling + self .offset ,
2484- self .ndigits )
2485-
2486- return RandFloat (min (min_val , max_val ), max (min_val , max_val ))
2535+ class BitScalingField (_ScalingField , BitField ):
2536+ """
2537+ A ScalingField that is a BitField
2538+ """
2539+ def __init__ (self , name , default , size , * args , ** kwargs ):
2540+ _ScalingField .__init__ (self , name , default , * args , ** kwargs )
2541+ BitField .__init__ (self , name , default , size )
24872542
24882543
24892544class UUIDField (Field ):
0 commit comments