Python 的 bit 級操作

在研究一些網路協定時,不乏有許多封包包裝定義,要實作勢必要使用到 bit 級的操作

雖然我工作上多有熟成的工具直接套用,所以都沒什機會用到這類操作,但總覺得還是要熟練一下 bit 操作,然後能自組封包內容,因此有了這篇筆記


Big-Endian v.s. Little-Endian


位元組順序 (Endianness) 分成兩種:

  • Big-Endian 大端序
  • Little-Endian 小端序

大端序最高位元組會存在最低的記憶體位址處,以 0x0A0B0C0D 的 32-bit int 值來說,0x0A byte 會存在記憶體較低的位置,0x0D byte 則會存在較高位置

/img/bit_op/big_endian.png

小端序則相反,最高位元組會存在最高的記憶體位址處,以 0x0A0B0C0D 的 32-bit int 值來說,0x0D byte 會存在記憶體較低的位置,0x0A byte 則會存在較高位置

/img/bit_op/little_endian.png

依處理器不同,可能採用大端序或小端序

  • x86、MOS Technology 6502、Z80、VAX、PDP-11等處理器為小端序
  • Motorola 6800、Motorola 68000、PowerPC 970、System/370、SPARC(除V9外)等處理器為大端序
  • ARM、PowerPC(除PowerPC 970外)、DEC Alpha、SPARC V9、MIPS、PA-RISC及IA64的位元組序是可調整的


網路傳輸一般採用大端序,也被稱之為網路位元組序,或網路序


如何表示 byte 值


1. 使用 b

b''   # 空 byte
b'\x10'  # 1 byte,二進制表示的話是 0001 0000
b'\x10\x00'  # 2 byte,二進制表示的話是 0001 0000 0000 0000

b'\x10' + b'\x10\x00'  # 3 byte

2. 使用 struct.pack(format, v1, v2, …)

import struct

struct.pack("B", 12)   # 1 byte,0000 1100 = \x0c
struct.pack(">i", 12)   # 4 byte,big-endian,\x00\x00\x00\x0c
struct.pack("<i", 12)   # 4 byte,little-endian,\x0c\x00\x00\x00

struct.pack(">Bi", 12, 12)   # 5 byte, big-endian, \x0c\x00\x00\x00\x0c'

struct.pack("B", 12) + struct.pack(">i", 12)  # \x0c\x00\x00\x00\x0c

format 格式字符


格式C 類型Python 類型標準大小
cchar長度為1的字節串1
bsigned char整數1
Bunsigned char整數1
?_Boolbool1
hshort整數2
Hunsigned short整數2
iint整數4
Iunsigned int整數4
llong整數4
Lunsigned long整數4
qlong long整數8
Qunsigned long long整數8
ffloatfloat4
ddoublefloat8

字節順序


字符字節順序大小對齊方式
<Little-Endian標準
>Big-Endian標準
!網路 = Big-Endian標準

3. 使用 bytearray


bytearray([1, 2, 3])   # \x01\x02\x03

bytearray(3)   # \x00\x00\x00

x = bytearray(b'\x01')
x.append(2)
x.append(3)  # \x01\x02\x03

bytearray(b'\x01') + bytearray(b'\x02')  # bytearray(b'\x01\x02')

從 byte 到數值


使用 struct.unpack(format, buffer)


struct.unpack('B', b'\x0c')   # tuple (12,)

a, b = struct.unpack('>Bi', b'\x0c\x00\x00\x00\x0c')  # a=12, b=12

參考




其他相關